Introduction to building (better) R packages

R/Pharma Conference 2025

Dr Nicola Rennie

Welcome!

About me

Data visualisation specialist, mainly using R, Python, and D3.


Background in statistics, operational research, and data science.


Author of several R packages, mainly for visualisation.

Grid of R package hex logos

What to expect during this workshop


  • Combines slides, live coding examples, and exercises for you to participate in.

  • Ask questions throughout!


Stranger Things questions gif

Source: giphy.com

Slides:
nrennie.rbind.io/r-pharma-2025-r-packages

Workshop requirements

Access to R environment:

  • Posit cloud
  • Your own installation of RStudio
  • Positron

Packages:

  • usethis
  • devtools
  • roxygen
  • stringr (optional)

Pre-requisites

  • You know a little bit of R
  • You know what R projects are
  • You might have written a function or two before

Getting started

What are R packages?

A collection of (some of) the following:

  • R functions
  • R objects
  • Data
  • Documentation
  • Tests
  • R Markdown / Quarto documents

that are arranged in a specific structure.

Why make packages?

Building a package makes it easier to:

  • run code
  • re-use code across different projects
  • share code with other people
  • read documentation rather than comments
  • manage dependencies and versions

But building an R package sounds like it’s…

I think you should leave gif

Source: giphy.com

  • for people who are more advanced than me
  • for people who have more functions that I do
  • only for people who make useful stuff
  • for code that’s ready to be shared
  • too complicated
  • scary

These are myths.

What’s in an R package?

Photo of recipe book

What’s in an R package?

cookbook/
├─ DESCRIPTION

Photo of recipe book

What’s in an R package?

cookbook/
├─ DESCRIPTION
├─ R/
│  ├─ tomato-bread.R
│  ├─ blueberry-muffins.R

Photo of recipe book

What’s in an R package?

cookbook/
├─ DESCRIPTION
├─ R/
│  ├─ tomato-bread.R
│  ├─ blueberry-muffins.R
├─ man/
│  ├─ tomato-bread.Rd
│  ├─ blueberry-muffins.Rd

Photo of recipe book

What’s in an R package?

cookbook/
├─ DESCRIPTION
├─ R/
│  ├─ tomato-bread.R
│  ├─ blueberry-muffins.R
├─ man/
│  ├─ tomato-bread.Rd
│  ├─ blueberry-muffins.Rd
├─ NAMESPACE

Photo of recipe book

What’s in an R package?

cookbook/
├─ DESCRIPTION
├─ R/
│  ├─ tomato-bread.R
│  ├─ blueberry-muffins.R
├─ man/
│  ├─ tomato-bread.Rd
│  ├─ blueberry-muffins.Rd
├─ NAMESPACE
├─ data/
├─ pkgdown/
├─ tests/
├─ vignettes/
├─ vignettes/
├─ README.md
├─ NEWS.md

Photo of recipe book

usethis

usethis is an R package that automates repetitive tasks that arise during project setup and development, both for R packages and non-package projects.

usethis hexsticker

Before we get started…

  • R Projects
    • a way to keep all your scripts, data, and settings for a project organised in one place
    • develop a package as a project
    • we don’t want to nest projects inside one another
  • Namespacing
    • we don’t want to use library()
    • you’ll see package::function() a few times

rproj icon

Live Demo!

  • Check we’re not in a project
  • Initialise the package
  • Look at the DESCRIPTION (metadata) file
  • What’s a license?
  • Install the package

Exercise 1

05:00
  • Run usethis::create_package("cookbook")
  • Edit the DESCRIPTION file
  • Run usethis::use_mit_license()
  • Install the package
    • What does devtools::install() do?

Adding functions

What’s a function?

  • A function has a name
  • It takes some inputs
  • Has some code that does something (to the input)
  • Returns some output
generate_hex <- function(format = "hex") {
  choices <- sample(
    x = c(as.character(0:9), LETTERS[1:6]),
    size = 6,
    replace = TRUE
  )
  output <- paste0("#", stringr::str_flatten(choices))
  if (format == "rgb") {
    output <- col2rgb(output)
  }
  return(output)
}

Adding to package

  • We add functions to a .R file
  • Usually called the same/similar name as the function
  • We add that file to the R/ folder
  • Unfortunately, we can’t use subfolders to organise our functions
  • But we can have multiple functions in one script

Live demo

  • Add the generate_hex() function to the package
  • Re-install the package
  • Does it work?

Loading with devtools

install()

  • installs your package into your R library like any other installed package
  • useful for installing packages that you’re ready to use

load_all()

  • simulates installation by loading all functions, data, and compiled code
  • useful for rapid development and testing

Live demo

  • Check environment is empty
  • Run devtools::load_all()
  • Does generate_hex() work now?

Exercise 2

05:00
  • Add a function to your R package
  • Run devtools::load_all() and test it works

If you need a function:

hello <- function(name) {
  output <- paste('Hello', name)
  return(output)
}

Writing documentation

Now we can use our function!

  • But other people who install the package can’t (yet)
  • We need to export our function
  • We can use roxygen2 to do that
    • write a shopping list (imports)
    • write a menu (exports)

roxygen2 hex sticker

Live Demo!

  • Add #' @export tags
  • Re-install and check it works
  • Inspect the namespace

Help files

What happens when you run ?ggplot2::geom_point?

Writing documentation with roxygen2

There are lots of different options for roxygen tags, the most common:

  • export: exports the function
  • param: what does an argument do? what are the defaults?
  • returns: what type of object does the function return?
  • examples: show the function in action

Live Demo!

  • Add roxygen tags
  • Run devtools::document()
  • View help files

Exercise 3

05:00
  • Use roxygen2 to export your function
  • Add more roxygen tags to document your function
  • Update the documentation with devtools::document()
  • Check the help files work

Checking and testing

Now we have

  • An R package
  • that contains a function we can use
  • with some nice documentation.

Is it ready to share with someone else?

We probably want to run some tests firsts

There are different types and levels of tests:

  • R CMD check:
    • do functions run?
    • do they all have documentation?
    • did you leave something behind?
  • Linting checks: is code well-formatted?
  • Unit tests: do functions work as they should? do you get an error when you expect one?
  • CRAN-checks: does it work across different systems, with most recent versions of every other package, and not break any other packages?

R CMD check

  • 1 error ✖ | 1 warning ✖ | 2 notes ✖
  • Errors, warnings, and notes don’t mean that your package doesn’t work!
  • It just means that other people might find it harder to use.
  • Sometimes it gets a little bit stuck!
    • Switching it on and off is a good starting point for weird errors.

Live Demo!

  • Run R CMD check
  • What’s broken?
    • Add imports (import, importFrom, namespacing)
    • usethis::use_package("stringr")
    • Edit DESCRIPTION (Depends, Imports, Suggests)

Exercise 4

05:00
  • Edit your function to use another package
  • Run R CMD check
  • Try to fix any errors and warnings

If you need a function:

hello <- function(name) {
  output <- paste('Hello', str_to_title(name))
  return(output)
}

More package building tools

Sharing packages with others

You can add your package to CRAN so that other can install it using install.packages("cookbook").

But CRAN is NOT the only way to share packages:

  • Use devtools (or remotes or pak) to install if it’s not on CRAN
    • Install from local copy: devtools::install_local("cookbook")
    • From GitHub: devtools::install_github("username/cookbook")

Git

  • If you’re not (yet) comfortable with using Git and GitHub, you can still upload the package files using Add file -> Upload files.

  • Git can help with managing versions of packages.

  • See happygitwithr.com for help getting started.

Package website

pkgdown hex sticker

Longer examples

Vignettes:

Hex stickers

Resources

Once you start making R packages…

Mary Berry gif

Source: giphy.com