Skip to content
AgileLogics
7 min read

Building R Packages

Prerequisites: Testing with testthat, Object-oriented R: S3

A package is the standard way to share R code — with colleagues, on GitHub, or on CRAN. Even for private work, packaging functions you reuse across projects gives you documentation, tests, and easy installation.

The usethis and devtools packages automate almost every step.

Creating a package

usethis::create_package("~/code/cleanr")
cleanr/
├── DESCRIPTION     # name, version, authors, dependencies
├── NAMESPACE       # generated: what the package exports
├── R/              # your functions
├── man/            # generated documentation
└── tests/testthat/ # tests

Writing a documented function

# R/clean_names.R

#' Standardize column names
#'
#' Converts names to lowercase snake_case and removes symbols.
#'
#' @param x A character vector of names.
#' @return A character vector of cleaned names.
#' @export
#' @examples
#' clean_names(c("First Name", "Total $"))
clean_names <- function(x) {
  x <- tolower(trimws(x))
  x <- gsub("[^a-z0-9]+", "_", x)
  gsub("^_|_$", "", x)
}
  • #' comments are roxygen2 documentation: running devtools::document() turns them into help pages (?clean_names) and updates NAMESPACE.
  • @export makes the function available to users; unexported functions are internal helpers.

The development loop

devtools::load_all()     # load the package as if installed (Ctrl+Shift+L)
devtools::document()     # rebuild documentation
usethis::use_test("clean_names")   # create tests/testthat/test-clean_names.R
devtools::test()         # run the tests
devtools::check()        # R CMD check: the full quality check
devtools::install()      # install it locally

Dependencies

Declare packages you use with usethis::use_package("dplyr"), which adds them to DESCRIPTION. Inside package code, call functions as dplyr::filter() — never use library() in a package.

Sharing

  • GitHub — usethis::use_github(); others install with pak::pak("you/cleanr"). usethis::use_github_action("check-standard") adds CI that runs R CMD check on every push.
  • CRAN — requires devtools::check() to pass with no errors, warnings, or notes, plus CRAN's policies; its reviewers check every submission.
  • pkgdown builds a documentation website from your help pages and vignettes.

Common mistakes

  • Using library() or setwd() inside package functions.
  • Relying on global variables. Package functions should get everything they need through their arguments.