Building and Publishing an R Package

· 5 min read · Updated March 10, 2026 · advanced
package-development cran devtools roxygen2 testthat

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:

  1. The function code
  2. Roxygen comments (lines starting with #')
  3. The @export tag, 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:

  1. Initialize a git repository in your package directory
  2. Create a repository on GitHub
  3. Connect your local repo to GitHub
  4. 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:

  1. Structure — use create_package() to set up directories
  2. Code — write functions in the R/ directory
  3. Document — add roxygen comments and run roxygenise()
  4. Test — create tests in tests/testthat/
  5. Check — run check() and fix all issues
  6. 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.