How to Build Simple R Packages: A Step-by-Step Guide
When you build simple R packages, sharing your R functions with colleagues or the community becomes straightforward. Turning your code into a package is the best way to do it. An R package bundles your functions into a format that’s easy to install, includes documentation, and works reliably.
This guide shows you the quickest path from zero to a working package you can share.
Why build a package?
R packages are more than just a distribution format. They force you to:
- Write clean, documented code
- Organize functions logically
- Test your work systematically
- Make your code reusable by yourself and others
Even for personal use, packages keep your utility functions organized and accessible.
The minimal package in 5 steps
Here is the fastest way to create a shareable R package.
Step 1: install development tools
You need a handful of packages for package development:
install.packages(c("devtools", "usethis", "roxygen2"))
library(devtools)
Step 2: create your package
Use usethis to set up the package structure automatically. The create_package() function generates a skeleton directory with all the required files and folders in place. This saves you from manually creating the DESCRIPTION, NAMESPACE, and R/ directory yourself, which would be tedious and error-prone. Once you run the command, you will have a working package skeleton in seconds.
# Create package in a new directory
create_package("~/myfirstpkg")
This creates a directory with the essential files that every R package must contain. Each of these files has a specific role in making your package work: the DESCRIPTION file holds metadata, the NAMESPACE file controls which functions are exported, the R/ directory stores your function source code, and the README provides an introduction for users. Let us look at what was generated:
myfirstpkg/
├── DESCRIPTION # Package metadata
├── NAMESPACE # What the package exports
├── R/ # Your functions go here
└── README.md # Introduction
Step 3: add your functions
Open the R/ folder and create a new file. Let us say you have a useful function for calculating confidence intervals. Each function you write should live in its own .R file inside the R/ directory, or you can group related functions together in a single file. The key thing is that every .R file in this directory is automatically sourced when your package loads. Here is a complete example with roxygen2 documentation:
# R/confint.R
#'\'' Calculate a Confidence Interval
#'\''
#'\'' Computes the confidence interval for a numeric vector.
#'\''
#'\'' @param x A numeric vector
#'\'' @param confidence The confidence level (default 0.95)
#'\'' @return A numeric vector of length 2
#'\'' @export
#'\''
#'\'' @examples
#'\'' ci <- confidence_interval(c(1, 2, 3, 4, 5))
#'\'' ci
confidence_interval <- function(x, confidence = 0.95) {
se <- sd(x) / sqrt(length(x))
alpha <- 1 - confidence
mean(x) + c(-1, 1) * qnorm(1 - alpha / 2) * se
}
The roxygen comments (lines starting with #') become your documentation automatically. Every @param tag describes a function argument, @return specifies the output, and @export ensures the function is available to users who load your package. The @examples block provides runnable sample code that appears in the help page. This inline documentation style keeps your code and docs together, making both easier to maintain.
Step 4: generate documentation
Run this command to turn your roxygen comments into proper help files:
document()
This creates .Rd files in the man/ directory. You can now access ?confidence_interval after installing. The .Rd files are R documentation format files that R’s help system reads. They contain the formatted versions of all your roxygen tags, structured so that ?function_name returns a proper help page in RStudio or the R console.
Step 5: install and test
Install your package locally:
install()
Now load it and use your function. Once the package is installed, library(myfirstpkg) makes all exported functions available in your R session. You can call your functions just like any built-in R function. The confidence interval output shows the lower and upper bounds of the 95% confidence interval for the given data, confirming that your package function works correctly as installed.
library(myfirstpkg)
# Test your function
confidence_interval(c(10, 12, 14, 16, 18))
# [1] 9.06 18.94
Congratulations, you have built a working R package! In just five steps you have created a project with proper structure, added a documented function, generated help files, and installed the result. Your package is now ready for further development and sharing with others.
Understanding the DESCRIPTION file
The DESCRIPTION file controls how your package appears to users. Open it and update the essentials:
Package: myfirstpkg
Title: What Your Package Does
Version: 0.1.0
Authors@R: person("Your", "Name", email = "you@example.com", role = c("aut", "cre"))
Description: A short description of what the package does.
License: MIT
Encoding: UTF-8
Imports:
stats
RoxygenNote: 7.2.3
Key fields to fill in:
- Title, a short sentence (not a full description)
- Description, one or more sentences about what the package does
- Authors@R, who owns and maintains the package
- License, MIT, GPL-3, or CC0 are common choices
The Imports field lists packages your code needs to run. Add any packages you use with library() or ::.
Sharing your package
Option 1: GitHub (recommended)
GitHub is the standard way to share R packages:
- Initialize a git repo:
usethis::use_git() - Create a GitHub repo:
usethis::use_github() - Push your code
Others can install directly from GitHub:
remotes::install_github("yourusername/myfirstpkg")
Option 2: CRAN
For widespread distribution, submit to CRAN:
- Run
check()and fix all issues - Read the CRAN submission policies
- Submit via the web form
CRAN review takes time but gives your package maximum visibility.
Option 3: Internal distribution
Not every package needs to go to CRAN or even GitHub. For teams, a private R package repository using drat or Posit Package Manager serves packages without public exposure. remotes::install_local("path/to/pkg") installs from a local directory for quick sharing during development, and pak::pkg_install("org/repo") installs directly from a private GitHub repository. The package structure is the same regardless of where the package lives.
Testing your package
Tests catch regressions and document expected behaviour. usethis::use_testthat() sets up the testing infrastructure with a single command. Run usethis::use_test("function_name") to create a test file for a specific function. Write assertions using testthat’s expect_*() family: expect_equal() for value comparisons, expect_error() to verify error handling, and expect_silent() to confirm code runs without issues.
test_that("calculation handles edge cases", {
expect_equal(my_function(c(1, 2, 3)), 6)
expect_error(my_function("invalid"))
})
Run devtools::test() to execute all tests. Each test runs in a fresh R session, so tests cannot accidentally depend on global state. A passing test suite gives you confidence to refactor and add features without breaking existing functionality. Even for packages that never leave your team, tests prevent regressions and serve as executable documentation of how each function should behave.
Next steps
Once you have mastered the basics, consider adding:
- Tests with testthat:
usethis::use_testthat()sets up the testing infrastructure and creates a sample test file. Testing catches regressions early and documents expected behaviour for future contributors. - Vignettes for long-form documentation using
usethis::use_vignette("my-vignette"). Vignettes show users how to solve real problems with your package, going beyond the function-level detail in help pages. - Data in the
data/directory viausethis::use_data(). Bundling example datasets helps users try your functions without preparing their own data first. - Continuous integration with GitHub Actions via
usethis::use_github_action(), which runs your tests automatically on every push and pull request.
See also
- Building and Publishing an R Package: comprehensive guide to the full workflow
- Writing R Packages with devtools — deeper dive into devtools patterns
- Testing R Code with testthat — write tests for your package
- Rcpp C++ Integration — add compiled code to your package
- R Documentation with roxygen2 — documentation best practices