R package development

software
r
r-package
Author

Sunny Hospital

Published

January 1, 2026

This is my learning journey from https://r-pkgs.org/whole-game.html.

# create package will create a basic dir structure
usethis::create_package("~/regexcite")

Create a function

Create a function and save it in R/strsplit1.R

strsplit1 <- function(x, split) {
    strsplit(x, split=split)[[1]] # safer than unlist
}

use_r creates .R file in the right place

use_r("strsplit1") # creates R/strsplit1.R

Now use it

load_all() simulates the process of building, installing, and attaching the regexcite package.

load_all()
devtools::load_all() # or use it with namespace

strsplit1("haha hoho", " ")

License

In DESCRIPTION page, you will see

License: use_mit_license(), use_gpl3_license() or friends to pick a license

Run it and it will create and add content to LICENSE file

use_mit_license() # check LICENSE file

Documentation

roxygen2 creates documentation from comments starting with #' in your R code.

#' Split a string
#'
#' @param x A character vector with one element.
#' @param split What to split on.
#'
#' @return A character vector.
#' @export
#'
#' @examples
#' x <- "alfa,bravo,charlie,delta"
#' strsplit1(x, split = ",")
strsplit1 <- function(x, split) {
  strsplit(x, split = split)[[1]]
}
document()

NAMESPACE change

NAMESPACE is what makes strsplit1 available to users after attaching regexcite via library(regexcite).

It’s possible to manually manage but we can use roxygen to manage it.

check() again

We previously had one warning about the license but we handled it. Now everything passes.

Install the package into library!

With the minimum viable product, we can install the package into your library.

install()

Now you can attach the package and use the function.

library(regexcite)
strsplit1("haha hoho", " ")

Unit test

We can perform unit test using use_testthat(). This initializes the unit testing machinery for your package. * adds comments to DESCRIPTION * creates a directory tests/testthat/ * adds a script test/testthat.R

use_test("strsplit1") # creates and opens a test file

Replace it with the following

test_that("strsplit1() splits a string", {
  expect_equal(strsplit1("a,b,c", split = ","), c("a", "b", "c"))
})

Run the test

test()

Note: tests will be run with check()

Use of external packages

You can use external package using use_package().

It will modify DESCRIPTION.

use_package("stringr") # adds stringr to Imports in DESCRIPTION

Modify the function

str_split_one <- function(string, pattern, n = Inf) {
  stopifnot(is.character(string), length(string) <= 1)
  if (length(string) == 1) {
    stringr::str_split(string = string, pattern = pattern, n = n)[[1]]
  } else {
    character()
  }
}

#Rename the files using rename_files()

rename_files("strsplit1", "str_split_one")

It’s important to document()

Remember that document() does two main jobs:

  • Converts our roxygen comments into proper R documentation.
  • (Re)generates NAMESPACE.

Modify test and run it

test_that("str_split_one() splits a string", {
  expect_equal(str_split_one("a,b,c", ","), c("a", "b", "c"))
})

test_that("str_split_one() errors if input length > 1", {
  expect_error(str_split_one(c("a,b","c,d"), ","))
})

test_that("str_split_one() exposes features of stringr::str_split()", {
  expect_equal(str_split_one("a,b,c", ",", n = 2), c("a", "b,c"))
  expect_equal(str_split_one("a.b", stringr::fixed(".")), c("a", "b"))
})

Now load to use the function

load_all()
str_split_one("a, b, c", pattern = ", ")

Load to Github

Add your package to your github using use_github()

Create readme using use_readme_rmd()

use_readme_rmd()
  • Creates README.md
  • Add lines in .Rbuildignore
  • Creates a git pre-commit hook to keep .Rmd and md in sync.

Modify readme

  • Modify .Rmd then run devtools::build_readme() to update README.md

See the example from the book

Create vignettes

Using usethis::use_vignette("my-vignette") does

  • creates vignettes/ directory
  • add content to DESCRIPTION
  • add content to .gitignore

Workflow

# 1.Create
usethis::use_vignette("my-vignette")

# 2. Add pros and code chunks

# 3. load
load_all()

# 4. render 
library(my-package)

note: check out install(build_vignettes = TRUE)

For more detailed vignettes info, go here!