| name | Package Dependencies |
| description | Complete guide to managing R package dependencies, including Imports vs Suggests, namespace imports, and handling dependencies in code, tests, examples, and vignettes |
Package Dependencies
Overview
Managing dependencies correctly is critical for R packages. This skill covers the different dependency types, how to declare and use them, and common patterns for conditional dependencies.
CRITICAL Concept: Listing vs Importing
Most important rule: Listing a package in Imports: does NOT make its functions available!
Imports:
dplyr
my_function <- function(data) {
filter(data, value > 0)
}
my_function <- function(data) {
dplyr::filter(data, value > 0)
}
my_function <- function(data) {
filter(data, value > 0)
}
Listing in DESCRIPTION ensures the package is installed.
Importing to NAMESPACE makes functions available in your code.
Dependency Types
Imports
Packages required for your package to work.
# DESCRIPTION:
Imports:
dplyr (>= 1.0.0),
rlang (>= 1.0.0),
tidyr
Guarantees:
- Installed when your package is installed
- Loaded when your package is loaded (but not attached)
- Functions available via
pkg::fun()
Use for:
- Packages your code depends on
- Packages used in most functions
- Critical dependencies
Usage pattern:
my_function <- function(x) {
dplyr::mutate(x, new_col = value * 2)
}
my_function <- function(x) {
x %>%
filter(value > 0) %>%
mutate(doubled = value * 2)
}
Suggests
Optional packages for enhanced functionality, tests, or documentation.
# DESCRIPTION:
Suggests:
ggplot2,
testthat (>= 3.0.0),
knitr,
rmarkdown,
covr
No guarantees:
- May or may not be installed
- Must check availability before use
- Cannot use
pkg::fun() without checking
Use for:
- Packages for optional features
- Testing frameworks (testthat, covr)
- Vignette builders (knitr, rmarkdown)
- Packages for examples only
- Heavy dependencies users may not need
Usage pattern:
my_plot <- function(data) {
if (!requireNamespace("ggplot2", quietly = TRUE)) {
stop("Package 'ggplot2' required but not installed.\n",
"Install with: install.packages('ggplot2')",
call. = FALSE)
}
ggplot2::ggplot(data, ggplot2::aes(x, y)) +
ggplot2::geom_point()
}
my_plot <- function(data) {
rlang::check_installed("ggplot2", reason = "to create plots")
ggplot2::ggplot(data, ggplot2::aes(x, y)) +
ggplot2::geom_point()
}
Depends
Makes another package's functions available in user's workspace (rarely recommended).
# DESCRIPTION:
Depends:
R (>= 4.1.0),
methods
Effects:
- Package is attached when yours is attached
- Functions available in user's search path
- Modifies user's environment
Modern usage:
R (>= version) - minimum R version (ALWAYS use this)
methods - if defining S4 classes
- Almost never use for other packages
Why avoid:
library(mypackage)
filter
Better approach:
Imports: dplyr
dplyr::filter(...)
LinkingTo
For packages with C/C++ code using headers from other packages.
# DESCRIPTION:
LinkingTo:
Rcpp,
RcppArmadillo
Use for:
- Rcpp packages
- Packages providing C++ headers
- Compiled code dependencies
Often combined with Imports:
Imports:
Rcpp (>= 1.0.0)
LinkingTo:
Rcpp
Config/Needs/*
Dependencies for development tools, not package functionality.
# DESCRIPTION:
Config/Needs/website:
pkgdown
Config/Needs/coverage:
covr
Config/Needs/development:
devtools,
usethis,
roxygen2
Use for:
- pkgdown for website
- covr for coverage
- Development tools
- CI-specific packages
Not installed by default:
pak::pak("mypackage", dependencies = TRUE)
Adding Dependencies
Using usethis Helpers
usethis::use_package("dplyr")
usethis::use_package("rlang", min_version = "1.0.0")
usethis::use_package("ggplot2", type = "Suggests")
usethis::use_import_from("dplyr", c("filter", "mutate", "select"))
usethis::use_package("R", min_version = "4.1.0", type = "Depends")
Manual Addition
# DESCRIPTION:
Imports:
dplyr (>= 1.1.0),
rlang (>= 1.0.0),
tidyr,
purrr
Suggests:
ggplot2 (>= 3.4.0),
testthat (>= 3.0.0)
Version specifications:
dplyr # Any version
dplyr (>= 1.0.0) # At least 1.0.0
dplyr (>= 1.0.0, < 2.0.0) # Rarely used, not recommended
Importing Functions to Namespace
Pattern 1: Explicit Namespace (Recommended)
my_function <- function(data) {
data %>%
dplyr::filter(value > 0) %>%
dplyr::mutate(doubled = value * 2) %>%
dplyr::select(id, doubled)
}
Advantages:
- Clear where functions come from
- No NAMESPACE management needed
- Easy to understand
- No import conflicts
Disadvantages:
- More typing
- Slightly verbose
Pattern 2: Selective Import (@importFrom)
my_function <- function(data) {
data %>%
filter(value > 0) %>%
mutate(doubled = .data$value * 2) %>%
select(id, doubled)
}
When to use:
- Functions used many times
- Infix operators (%>%, %||%, :=)
- Core package dependencies
- Reduces verbosity
Where to put @importFrom:
my_function <- function() { ... }
"_PACKAGE"
NULL
Pattern 3: Full Import (@import) - Rare
Only for:
- rlang (if building tidy evaluation package)
- Your own internal package
Avoid for most packages:
- Namespace pollution
- Potential conflicts
- Unclear provenance
Operators and Infix Functions
Always import operators:
data %>% dplyr::filter(x > 0)
data %>% dplyr::filter(x > 0)
data |> dplyr::filter(x > 0)
Common operators to import:
Using Dependencies in Different Contexts
In Package Code (R/)
my_function <- function(data) {
filter(data, value > 0)
}
my_function <- function(data) {
dplyr::filter(data, value > 0)
}
my_optional_feature <- function(data) {
rlang::check_installed("ggplot2", reason = "for plotting")
ggplot2::ggplot(data, ggplot2::aes(x, y)) +
ggplot2::geom_point()
}
In Examples (@examples)
In Tests (tests/testthat/)
test_that("function works", {
result <- my_function(data)
expect_equal(result$value, expected)
})
test_that("plotting works", {
skip_if_not_installed("ggplot2")
plot <- my_plot(data)
expect_s3_class(plot, "gg")
})
test_that("integration with optional package", {
skip_if_not_installed("dplyr")
library(dplyr)
result <- data %>%
my_transform() %>%
summarize(mean = mean(value))
expect_equal(result$mean, 5)
})
In Vignettes (vignettes/)
Alternative: Use separate vignettes:
# DESCRIPTION:
Suggests:
knitr,
rmarkdown,
ggplot2
# vignettes/basic-usage.Rmd - no optional deps
# vignettes/advanced-plotting.Rmd - requires ggplot2
In Documentation (roxygen2)
Minimum Version Specifications
When to Specify Versions
# Always specify if you need specific features: