R Package Structure
Every R package follows a fixed directory layout. This isn’t a style preference — it is a convention defined by the R packaging system and enforced by R CMD check. Packages accepted by CRAN, Bioconductor, and other repositories all share the same structure because the installation and check machinery expects it. Understanding this layout makes you a better package developer and a better R user, since you can always predict where to look for code, documentation, or data inside any package you install.
The DESCRIPTION File
The DESCRIPTION file is the package manifest. It tells R everything about your package: who wrote it, what version it is, what it does, and what it depends on. R CMD check reads it first.
Package: mypackage
Title: What the Package Does
Version: 0.1.0
Authors@R: person("Jane", "Doe", , "jane@example.com", role = c("aut", "cre"))
Description: A short paragraph explaining what the package does.
License: MIT
Imports:
dplyr,
tidyr
Suggests:
testthat
Depends: R (>= 4.0)
The Package name must match the directory name exactly. Title is a one-line summary in title case. Version follows the familiar three-number format. The Authors@R field is the modern way to credit authors — aut lists contributors and cre marks the maintainer.
The dependency fields cause the most confusion. Imports lists packages required at runtime; they are loaded but not attached to the search path. Suggests lists packages only needed during development, testing, or vignette building — things like testthat, knitr, and rmarkdown. Depends is where you put the minimum R version. Avoid listing other packages in Depends — Imports is almost always the better choice.
The NAMESPACE File
The NAMESPACE file controls what your package exports to users and what it imports from other packages. Without exports, users cannot access your functions. Without imports, your package fails the moment it tries to call an external function.
export(my_function)
exportPattern("^[^\\.]")
import(dplyr)
importFrom(dplyr, filter, select)
export(my_function) makes a specific function available to users. exportPattern("^[^\\.]") is a shortcut that exports every function whose name does not start with a dot. import(dplyr) imports everything dplyr exports — convenient but imprecise. importFrom(dplyr, filter, select) is better practice: it imports only the specific functions you name, keeping your namespace clean.
In practice, you rarely write NAMESPACE by hand. devtools::document() reads roxygen2 comments in your R/ files and regenerates NAMESPACE automatically.
The R/ Directory
All R source code lives in R/. Each file typically groups related functions together — utils.R for helpers, core.R for the main exported functions, internal.R for functions users should not access.
# R/utils.R
#' Add one to a number
#' @param x A numeric vector.
#' @return A numeric vector with each element incremented by one.
#' @export
add_one <- function(x) {
x + 1
}
Exported functions get roxygen2 comments above them. Unexported internal functions start with a dot by convention — .helper() is internal use only. Running devtools::document() turns those roxygen2 comments into .Rd documentation files in man/.
The man/ Directory
Documentation files in man/ are generated from roxygen2 comments in R/. You do not write .Rd files by hand — they get overwritten every time you run devtools::document(). The workflow is: write your function with roxygen2 comments, run document(), and the documentation appears.
The tests/ Directory
Formal tests live in tests/testthat/. The testthat package is the standard testing framework for R packages.
# tests/testthat/test-utils.R
library(testthat)
library(mypackage)
test_that("add_one works correctly", {
expect_equal(add_one(1), 2)
expect_equal(add_one(c(1, 2)), c(2, 3))
expect_equal(add_one(numeric()), numeric())
})
R CMD check runs your test suite automatically. A test runner file called testthat.R at the top of tests/ orchestrates everything.
The inst/ Directory
inst/ holds files that get copied verbatim into the installed package’s top-level directory. Use it for sample data files, configuration files, fonts, or any static asset your package needs at runtime. Access files with system.file("myfile", package = "mypackage"). Anything that is not R code or a shipped dataset but needs to exist at runtime belongs in inst/.
The data/ Directory
data/ stores R binary files (.rda) containing datasets shipped with the package. These are the kind you access with data(mtcars). They are lazy-loaded automatically in installed packages. For raw data files you want users to access at runtime, put them in inst/extdata/ instead.
The src/ Directory
src/ holds compiled code — C, C++, or Fortran. Most R packages do not need this. It exists for performance-critical operations where R’s interpreted code is too slow. If your package does not use compiled code, you do not need a src/ directory at all.
The vignettes/ Directory
Vignettes are R Markdown files that provide narrative documentation — tutorials, use-case walkthroughs, and explanations that go beyond what a function reference can offer. Place .Rmd files in vignettes/ and they get built into HTML or PDF during devtools::check() or devtools::build_vignettes(). The rendered output ends up in inst/doc/ after installation. Vignettes are the gold standard for showing how a package works in practice.
The LICENSE File
LICENSE contains the full license text. If you specify License: MIT in DESCRIPTION, the LICENSE file must contain the actual MIT license text. CRAN requires the full text for GPL-family licenses, and it is standard practice for MIT and BSD licenses too.
The Development Loop
Two devtools functions drive the package development workflow.
devtools::load_all() simulates installing the package without actually installing it. It loads all code from R/, runs roxygen2, and sets up a temporary namespace. It is fast — designed for interactive development where you want to test changes immediately.
devtools::load_all(path = ".")
When you are ready to validate everything, devtools::check() runs the full R CMD check suite. This is a comprehensive validation: DESCRIPTION consistency, NAMESPACE validity, code quality, test execution, compiled code compilation, and vignette building. Running check() before submitting to CRAN or sharing your package is not optional — it is how you find the problems that will get your package rejected.
Putting It Together
Here is how the pieces connect. On disk, a package looks like this:
mypackage/
DESCRIPTION # Package manifest
NAMESPACE # Imports and exports
R/ # Source code
man/ # Auto-generated documentation
tests/
testthat/
testthat.R
inst/
extdata/
data/
vignettes/
src/
LICENSE
When the package is built and installed, the contents of inst/ are copied to the package root, vignettes are rendered to inst/doc/, and everything in data/ becomes lazy-loadable. R CMD check enforces this layout. Files or directories outside this structure produce warnings or errors.
The R package structure is a convention that makes R packages portable and reproducible. The same layout works whether you are building a small personal utility, a package for your team, or something you intend to publish on CRAN.
See Also
- R Basics: Vectors and Types — Core R data structures
- Functions and Control Flow — Writing your own R functions
- Data Frames and Tibbles — Working with tabular data in R