Building pkgdown Sites

· 9 min read · Updated March 31, 2026 · intermediate
r pkgdown package-development documentation

pkgdown turns the standard documentation files in your R package into a full-featured website. It reads your DESCRIPTION, your roxygen comments, your vignettes, and your README, then assembles them into a site with a navbar, a reference index, article listings, and a home page. The output is static HTML, which means it deploys cheaply and reliably on GitHub Pages, Netlify, or any web host.

This tutorial walks through the full workflow: setting up the configuration files, building the site locally, writing additional articles, and deploying to three different hosting targets.

Prerequisites

Your package needs a few things in place before pkgdown can do its job:

  • A valid DESCRIPTION file with Title, Version, and Authors@R fields
  • roxygen2 documentation in your R/ source files (pkgdown generates the reference pages from roxygen blocks)
  • Optionally, one or more vignettes in vignettes/

If you have an existing package built with devtools, you likely already have all of this. If not, the R Package Structure tutorial covers the fundamentals.

Installing pkgdown

pkgdown is not a dependency of your package — it is a build tool you install separately:

install.packages("pkgdown")

Always use the latest version. pkgdown develops quickly and the output format changes occasionally:

install.packages("pkgdown", repos = "https://cran.rstudio.com")

Initial Setup

Run pkgdown::build_site() from your package root. On a freshly configured package, this does everything in one step:

pkgdown::build_site()

pkgdown creates a docs/ directory containing the complete static site. It also creates a _pkgdown.yml configuration file in your package root if one does not already exist. That configuration file is where you control the site’s structure, appearance, and content.

The first build reads your DESCRIPTION to populate the navbar and home page automatically. You get a working site immediately, then refine it by editing _pkgdown.yml.

The _pkgdown.yml Configuration File

_pkgdown.yml controls the entire site. Here is a minimal configuration for a package called mypkg:

url: https://user.github.io/mypkg

template:
  package: default
  bootswatch: flatly

navbar:
  structure:
    left:
      - home
      - articles
      - reference
      - news
    right: github

reference:
  - title: "Main functions"
    contents:
      - starts_with("^[^_]")

articles:
  - title: "Guides"
    contents:
      - getting_started
      - advanced_usage

Key fields

url is required if you plan to deploy to GitHub Pages or any hosted environment. pkgdown uses it to generate correct canonical URLs and sitemaps. Without it, relative links work fine for local preview but break in production.

template controls the visual theme. The bootswatch field swaps colour schemes. Valid values include flatly, cerulean, journal, readable, spacelab, united, and cosmo. The default template uses Bootstrap 5 and produces clean, readable output without additional dependencies.

navbar defines the top navigation bar. The structure section lists the built-in pages and their order. You can add custom links and dropdown menus here.

reference maps to the auto-generated function reference. The contents field accepts individual function names, starts_with() patterns, and matches() regex patterns.

articles lists the vignettes you want to appear in the articles section. pkgdown reads your vignettes/ directory and matches entries by the filename stem.

Customising the Home Page

The home page (index.md or README.Rmd in the package root) becomes the site’s landing page. pkgdown extracts the content after the badges and places it on the site home.

Write your README as usual, but include a <!-- pkgdown: home: start --> and <!-- pkgdown: home: end --> comment pair to control what appears on the site:

<!-- pkgdown: home: start -->
# mypkg

{mypkg} makes it easy to do X with R.

[![CRAN](https://www.math.puz.io/badges/pick.svg)](https://cran.r-project.org/package=mypkg)
[![R build status](https://github.com/user/mypkg/workflows/R-CMD-check/badge.svg)](https://github.com/user/mypkg/actions)

## Installation

```r
install.packages("mypkg")

Quick start

library(mypkg)
result <- do_something(data)

Anything outside those markers — badge HTML, build status images, installation instructions that repeat elsewhere — is stripped from the site home. This keeps the generated page clean and focused.

## Customising the Navigation Bar

The navbar is defined in the `navbar` section of `_pkgdown.yml`. Beyond the built-in pages (`home`, `articles`, `reference`, `news`), you can add external links, section headings, and dropdown menus:

```yaml
navbar:
  structure:
    left:
      - home
      - articles
      - reference
      - news
      - separator
      - text: "FAQ"
        href: "https://example.com/faq"
      - github
  components:
    github:
      icon: fa-github fa-lg
      href: https://github.com/user/mypkg

The separator inserts a visual divider between the built-in pages and any custom links. Dropdown menus are also possible:

navbar:
  structure:
    left:
      - home
      - articles
      - reference
      - dropdown:
          text: "More"
          menu:
            - text: "Changelog"
              href: news/index.html
            - text: "FAQ"
              href: https://example.com/faq
            - text: "Support"
              href: https://github.com/user/mypkg/issues

Adding Articles

Articles in pkgdown are vignettes. pkgdown renders .Rmd files in vignettes/ to HTML and places them under the /articles/ path of your site.

Create a vignette as you normally would with usethis::use_vignette("getting-started"):

usethis::use_vignette("getting-started")

This creates vignettes/getting-started.Rmd. Edit it as normal R Markdown. The vignette title in the YAML frontmatter becomes the article title on the site:

---
title: "Getting Started with mypkg"
output: rmarkdown::html_vignette
vignette: >
  %\VignetteIndexEntry{Getting Started with mypkg}
  %\VignetteEngine{rmarkdown::html_vignette}
  %\VignetteEncoding{UTF-8}
---

# Introduction

This vignette shows you how to...

To expose the vignette in the site, add it to the articles section of _pkgdown.yml:

articles:
  - title: "User Guides"
    contents:
      - getting_started
      - advanced_usage

If your vignettes directory contains files not listed in articles, they still appear in a catch-all “Articles” section unless you set dir: ... to a custom location. Listing them explicitly gives you control over grouping and ordering.

After adding or modifying vignettes, rebuild the site:

pkgdown::build_articles()

Controlling the Reference Index

The reference page lists every documented object in your package. By default, pkgdown includes all functions and datasets that have roxygen blocks. You can organise them into groups using roxygen tags:

#' @family geocoding functions
geocode <- function(address) {
  # ...
}

Then reference the family in _pkgdown.yml:

reference:
  - title: "Geocoding"
    desc: "Convert addresses to coordinates"
    contents:
      - geocode
      - reverse_geocode

  - title: "Data"
    contents:
      - starts_with("^data_")

  - title: "Internal"
    contents:
      - matches("^\\._[^_]")
    auto: false

The auto: false setting excludes these objects from the auto-generated index. Objects without an explicit family appear in an “Other” group.

The News Page

pkgdown automatically builds a news page from NEWS.md in your package root. Use second-level headings to separate versions:

# mypkg 0.3.0

* Added `geocode()` function for forward geocoding
* Fixed bug in `import_data()` when input has missing values

# mypkg 0.2.0

* New vignette on advanced usage patterns
* Dropped support for R 3.x

The most recent heading becomes the top section of the news page. Each version gets its own expandable section on the rendered site.

Building Locally

During development, build individual parts rather than the full site for speed:

# Build only the reference pages
pkgdown::build_reference()

# Build only the home page
pkgdown::build_home()

# Build only the articles
pkgdown::build_articles()

# Build the news page
pkgdown::build_news()

Run pkgdown::build_site() for a full production build. The output goes into docs/ by default.

Preview the site locally with pkgdown::preview_site(). This opens the docs/index.html file in your browser.

Add docs/ to your .gitignore if you deploy through GitHub Actions (see the next section). If you commit the docs/ directory directly, you do not need the .gitignore entry.

Deploying to GitHub Pages

GitHub Pages serves files from the gh-pages branch or from a docs/ directory on main or gh-pages. The simplest approach for pkgdown uses a GitHub Actions workflow that rebuilds and deploys the site on every push.

Create .github/workflows/pkgdown.yaml:

on:
  push:
    branches: [main, master]
  workflow_dispatch:

name: pkgdown

jobs:
  pkgdown:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - uses: r-lib/actions/setup-r@v2
        with:
          use-public-rspm: true

      - uses: r-lib/actions/setup-r-dependencies@v2
        with:
          extra-packages: |
            any::pkgdown
            local::.

      - name: Build site
        run: pkgdown::build_site_github_pages(new_version = "development")

      - name: Deploy to GitHub Pages
        uses: peaceiris/actions-gh-pages@v3
        with:
          github_token: ${{ secrets.GITHUB_TOKEN }}
          branch: gh-pages
          directory: docs

This workflow uses build_site_github_pages() from pkgdown, which handles the version separation that lets you deploy both a development snapshot and a release snapshot simultaneously. It also handles the commit history on the gh-pages branch correctly.

Enable GitHub Pages in your repository settings under Settings → Pages → Source, and select the gh-pages branch and / (root) directory.

Deploying to Netlify

Netlify deploys from a publish directory. Configure your _pkgdown.yml to output there instead of docs/:

destination: publish

Then create a netlify.toml in your repository root:

[build]
  command = "Rscript -e \"pkgdown::build_site()\""
  publish = "publish"

Connect your repository in the Netlify UI, set the production branch, and Netlify runs the build command on every push. The generated publish/ directory becomes your site.

One advantage of Netlify is custom subdomain handling built into the dashboard. You do not need to configure anything in _pkgdown.yml beyond the url field.

Deploying to a Subdomain

All three hosting targets support subdomains. Set the url field in _pkgdown.yml to your full subdomain:

url: https://docs.mypackage.com

For GitHub Pages with a custom domain, add a CNAME file inside docs/ (or in the root of your gh-pages branch):

docs.mypackage.com

Then configure the custom domain in GitHub Pages settings. DNS should point to user.github.io via an A record or CNAME.

On Netlify, add the subdomain in Site settings → Domain management → Add custom domain. Netlify handles SSL automatically through Let’s Encrypt.

pkgdown generates absolute URLs when url is set. This ensures that sitemap.xml, Open Graph tags, and canonical links all point to the correct address, which matters for SEO and for social previews when sharing links.

Ignoring Files in the Site

Some files in your package should not appear in the built site. Use the exclude field in _pkgdown.yml:

exclude:
  - "^NEWS.md$"
  - "^cran-comments.md$"
  - "inst/tinytest"

By default, pkgdown already excludes common non-code files like .travis.yml, .Rbuildignore patterns, and files starting with _ or .. The exclude field handles package-specific files.

Using a Custom Template

For more control over the visual design, you can pass a custom pkgdown template package. Several community template packages exist, or you can create your own. To use one from GitHub:

template:
  package: myorg/pkgdownthemes
  bootswatch: cerulean
  includes:
    in_header: include/google-analytics.html

The includes field injects arbitrary HTML into the page head, footer, or navbar. Common uses include Google Analytics tracking code, Hotjar snippets, or custom favicons.

Best Practices

Keep vignettes focused. Each vignette should cover one task or concept. Users arriving from a search engine usually want a specific answer quickly. Long narrative vignettes are harder to skim than short focused ones.

Link between reference and articles. Use relative links in your vignettes: [do_something()][do_something] links to the function reference. pkgdown resolves these at build time.

Update the site in your release workflow. Tag a release, then push. The GitHub Actions workflow builds the release snapshot automatically. Your users always see current documentation at the stable URL.

Use semantic versioning for the news. The news page parses version numbers from headings. Inconsistent formatting breaks the grouping. Write {# .version 0.3.0} as an attribute if you need precise control:

# mypkg 0.3.0 {# .version 0.3.0}

Preview before deploying. Run pkgdown::check_pkgdown() to validate links and structure without building the full site. This catches missing references and broken article paths early.

Set destination explicitly for non-GitHub deploys. If you use Netlify, RsConnect, or any target that is not GitHub Pages, set destination: docs (or your chosen directory) explicitly. pkgdown defaults work for GitHub Pages but may not match your deploy configuration.

See Also