Building and Publishing an R Package
R packages are the fundamental unit of shareable code in the R ecosystem. They bundle functions, data, and documentation into a format that other users can easily install and use. Building a package might sound intimidating, but the R tools ecosystem makes the process straightforward.
This guide walks you through creating a complete R package from scratch. You’ll learn how to structure your package, write documentation that users can understand, add tests that catch bugs, and share your work with the community.
Setting Up Your Package Structure
The first step is creating the basic directory structure. The usethis package provides helper functions that set up everything for you.
install.packages(c("devtools", "usethis", "roxygen2", "testthat"))
library(usethis)
# Create a new package project
create_package("~/mypackage")
This creates a directory with the standard R package structure:
mypackage/
├── DESCRIPTION # Package metadata
├── NAMESPACE # Exports and imports
├── R/ # Your R code goes here
├── man/ # Generated documentation
├── tests/ # Test files
│ └── testthat/
└── README.md # Introduction
The key files you need to understand are DESCRIPTION and NAMESPACE.
The DESCRIPTION File
The DESCRIPTION file contains metadata about your package. It tells R who wrote the package, what it does, what other packages it depends on, and how it should be licensed.
Package: mypackage
Title: What the Package Does
Version: 0.1.0
Authors@R: person("First", "Last", email = "first.last@example.com", role = c("aut", "cre"))
Description: A short description of what the package does.
License: MIT
Encoding: UTF-8
Imports:
dplyr,
ggplot2
Suggests:
testthat
RoxygenNote: 7.2.3
The most important fields are:
- Package — the name of your package (no spaces)
- Title — a short title (not a full sentence)
- Description — a paragraph explaining what the package does
- Imports — packages your package needs to function
- Suggests — packages needed for development or optional features
Writing Package Code
Put your R functions in the R/ directory. Each file should contain related functions. Use meaningful filenames that describe what the functions do.
# R/greeting.R
#' Create a personalized greeting
#'
#' @param name The name to include in the greeting
#' @return A character string containing the greeting
#' @export
#' @examples
#' greet("World")
greet <- function(name) {
paste0("Hello, ", name, "!")
}
This example shows the three components of a well-documented function:
- The function code
- Roxygen comments (lines starting with
#') - The
@exporttag, which makes the function available to users
Documentation with roxygen2
Roxygen2 converts special comments in your R code into help pages. This keeps your documentation next to the code it documents, making it easier to maintain.
The most common roxygen2 tags are:
- @param name description — describes each parameter
- @return description — describes what the function returns
- @export — makes the function available to users
- @examples — runnable code shown in the help page
- @importFrom package function — imports a specific function from a package
#' Calculate the area of a circle
#'
#' @param radius The radius of the circle
#' @param pi_value The value of pi to use (defaults to base pi)
#' @return The area of the circle
#' @export
#' @examples
#' circle_area(5)
#' circle_area(3.14, pi_value = 3.14159)
circle_area <- function(radius, pi_value = pi) {
pi_value * radius^2
}
After adding roxygen comments, run this to generate the documentation:
library(roxygen2)
roxygenise()
This creates .Rd files in the man/ directory. These are the help files that users see when they run ?greet.
The NAMESPACE File
The NAMESPACE file controls what functions are available to users of your package. The @export tag in your roxygen comments automatically adds functions to the NAMESPACE.
You rarely need to edit NAMESPACE directly. The roxygen2 process handles it for you.
However, you should understand the difference between:
- Exports — functions users can call directly (
greet()) - Imports — functions available internally but not for users
Testing with testthat
Tests verify that your code works correctly. The testthat package provides a testing framework inspired by unittest frameworks in other languages.
Create a test file in tests/testthat/:
# tests/testthat/test-greeting.R
library(testthat)
library(mypackage)
test_that("greet returns correct format", {
result <- greet("Alice")
expect_type(result, "character")
expect_match(result, "Alice")
expect_match(result, "Hello")
})
test_that("greet handles different names", {
expect_equal(greet("Bob"), "Hello, Bob!")
expect_equal(greet("World"), "Hello, World!")
})
Run tests during development:
library(devtools)
test()
Good tests cover:
- Expected behavior with normal inputs
- Edge cases and boundary conditions
- Error handling for bad inputs
Write tests as you develop, not after. Test-driven development (write the test first, then the code) often produces cleaner code.
Building and Checking Your Package
Before sharing your package, you must make sure it passes all checks. The R CMD check command runs extensive tests.
library(devtools)
check()
This validates:
- Package structure and metadata
- R code syntax and style
- Documentation completeness
- Test results
- Examples run without errors
- Package installs correctly
Fix every issue that check() reports. CRAN runs similar checks when you submit a package.
Version Numbering
R packages use semantic versioning: major.minor.patch. Increment:
- Major for breaking changes
- Minor for new features (backward compatible)
- Patch for bug fixes
Your initial version should be 0.1.0. Move to 1.0.0 when the package is stable and feature-complete.
Publishing to GitHub
GitHub is the easiest place to share your package during development. Users can install directly from GitHub using devtools:
devtools::install_github("yourusername/mypackage")
To publish to GitHub:
- Initialize a git repository in your package directory
- Create a repository on GitHub
- Connect your local repo to GitHub
- Push your code
usethis::use_git()
usethis::use_github()
Publishing to CRAN
The Comprehensive R Archive Network (CRAN) is the official repository for R packages. Submitting to CRAN makes your package available to all R users through install.packages().
Before submitting:
- Run
check()and fix all errors and warnings - Ensure documentation is complete
- Test on multiple machines if possible
- Read the CRAN submission policies
Submit via the CRAN submission form. Maintainers will review your package and provide feedback.
After approval:
- Maintain your package (fix bugs, update for new R versions)
- Respond to user issues and bug reports
- Consider adding co-authors for significant contributions
Installing Your Package Locally
During development, install your package frequently to test it:
library(devtools)
install()
This reinstalls the package from the current directory. Use load_all() to load code without reinstalling — faster for rapid development.
Summary
Building an R package involves several steps:
- Structure — use
create_package()to set up directories - Code — write functions in the
R/directory - Document — add roxygen comments and run
roxygenise() - Test — create tests in
tests/testthat/ - Check — run
check()and fix all issues - Share — publish on GitHub or CRAN
The investment in creating a proper package pays off through better code organization, automatic documentation, reliable tests, and easy sharing with others.