Documenting with roxygen2

· 6 min read · Updated March 30, 2026 · intermediate
r packages roxygen2 devtools cran

Every R package that appears on CRAN ships with .Rd documentation files in its man/ directory. These are the files R fetches when a user calls ?mean or help("subset"). Before roxygen2 existed, you had to write those .Rd files by hand using a cryptic markup syntax. That was error-prone and annoying. roxygen2 changed that by letting you write documentation in regular R comments, then generating the .Rd files automatically.

The idea is simple: put special #' comment blocks above your functions in the R/ directory, run devtools::document(), and out come properly formatted .Rd files. Your source code and documentation live side by side, which makes maintenance easier. This is now the standard approach for R package development.

How roxygen Blocks Work

A roxygen block is a sequence of lines that start with #'. Place one directly above a function definition in any file under R/:

#' Sum of vector elements
#'
#' `sum` returns the sum of all the values present in its arguments.
#'
#' @param ... Numeric values to sum.
#' @param na.rm Logical. If TRUE, NA values are removed.
#' @return A single numeric value.
#' @examples
#' sum(c(1, 2, 3))
#' sum(c(1, NA), na.rm = TRUE)
#' @export
sum <- function(..., na.rm = FALSE) {
  sum(c(...), na.rm = na.rm)
}

The block sits above the function. Roxygen2 reads every #' line in your R/ directory, parses the content, and writes a corresponding .Rd file to man/. It also updates NAMESPACE based on @export and @importFrom tags.

A block has two parts. The free-form text before the first @ tag forms the introduction. The first line is the title (one sentence, sentence case, no period). The second paragraph is the description. Any paragraphs after that form the details section. Then the @ tags describe specific components like parameters, return values, and examples.

The Essential Tags

@param, @return, @examples, and @export are the tags you will reach for on every function.

Documenting Parameters

Use @param to describe each argument. The format is @param name type description — type is optional but recommended:

#' @param x A numeric vector.
#' @param trim Real number between 0 and 0.5. Amount to trim from each tail.

If multiple parameters share a description, list them separated by commas: @param x,y Numeric vectors.

The description should tell users what the parameter does, not just its type. “A numeric vector” is less useful than “A numeric vector of values to summarise.”

Describing Return Values

@return documents what the function gives back. Keep it brief — state the type and shape, not the implementation:

#' @return A numeric vector of length one.

For functions that return different types depending on input, describe the common case and note variations in details.

Writing Examples

Examples are the most valuable part of documentation — but they must run without error. R CMD check executes them automatically, so broken examples fail the check.

#' @examples
#' mean(c(1, 2, 3))
#' mean(c(1, NA), na.rm = TRUE)

If an example should error under normal use, wrap it in try() or \dontrun{}:

#' @examples
#' \dontrun{
#' read_csv("nonexistent.csv")
#' }

For examples that should only run in an interactive session (like ones requiring user input), use @examplesIf interactive() instead.

Making Functions Available with @export

Without @export, a function is invisible to package users — it exists in the code but cannot be accessed via library(mypackage). Adding @export above a function does two things: it generates an export() directive in NAMESPACE, and it causes roxygen2 to create a user-facing help page for that function.

#' @export
my_function <- function(x) {
  x + 1
}

Internal helper functions that users should not call directly can omit @export. You can suppress the .Rd file entirely with @noRd if you want to keep the roxygen comments for developer reference without creating a help page.

Importing from Other Packages

Use @importFrom to pull in specific functions from other packages without loading the whole package with library(). This goes in the roxygen block of whichever function uses the imported function, or at the package level (above NULL or in a dedicated file like R/utils.R):

#' @importFrom magrittr %>%
#' @importFrom rlang .data
NULL

Running devtools::document() adds the corresponding importFrom() directives to NAMESPACE and updates the Imports: field in DESCRIPTION automatically.

Markdown in roxygen2

Since roxygen2 version 6.0, Markdown is supported. Enable it globally by adding to your DESCRIPTION:

Roxygen: list(markdown = TRUE)
```yaml

Or enable it per-block with the `@md` tag at the start of a block. Markdown lets you use backticks for inline code, fenced code blocks, bold, italics, and hyperlinks:

```r
#' @md
#' The `mean()` function supports [trimmed means][base::mean].

If you enabled Markdown globally but need to disable it for a specific block, use @noMd.

Generating Documentation

Once your roxygen blocks are in place, generate the .Rd files by running:

devtools::document()

This is the equivalent of calling roxygen2::roxygenise(). The function reads every #' block in R/, parses all tags, writes .Rd files to man/, and updates NAMESPACE. You will run this command hundreds of times while developing a package — it is idempotent, so running it again does no harm.

You generally never edit the generated .Rd files by hand. They get overwritten every time you call document(). If you need to add content that roxygen2 cannot express, edit the roxygen source block and re-run document().

The @seealso tag adds a “See Also” section to the generated help page, linking to other topics:

#' @seealso [mean()] for the generic, [var()] for variance.

Use @family to group related functions together. Every function tagged with @family utility appears in every other function’s auto-generated “See Also” section:

#' @family utility functions

This is useful for a set of helper functions that serve related purposes.

Common Patterns

Inheriting Parameter Documentation

If your function wraps another and accepts the same parameters, use @inheritParams to copy the documentation:

#' @inheritParams base::merge

This avoids repeating the same descriptions and keeps documentation consistent across a package.

Documenting Multiple Functions Together

Use @describeIn and @rdname to share documentation across multiple functions or to document method-specific behavior in a single block:

#' @param x Numeric vector.
#' @describeIn my_generic
#' Description of the specific method behavior.
my_method <- function(x) { ... }

This puts both the generic and the method in the same .Rd file under different sections.

What Gets Generated

When you run devtools::document(), the tool produces a .Rd file for every exported function that has a roxygen block. The file lives in man/ and follows the standard R documentation format. It also produces NAMESPACE entries from @export and @importFrom tags, and updates DESCRIPTION fields like Imports: and RoxygenNote.

If you are submitting to CRAN, the documentation must pass R CMD check without warnings. That means every exported function needs a title, a description, @param for every argument, and @return. Examples must run cleanly. Getting this right with roxygen2 is straightforward — the tags map directly to the requirements.

See Also