Code Quality with lintr and styler

· 5 min read · Updated March 18, 2026 · beginner
r-programming lintr styler code-quality package-development

Writing clean, consistent code is essential for any R project, especially when building packages for others to use. Two packages from the R ecosystem help you achieve this: lintr for static code analysis and styler for automatic code formatting. This guide walks you through installing and using both tools effectively.

Installing lintr and styler

Both packages are available on CRAN and can be installed with a simple call to install.packages(). You can also install the development versions from GitHub if you need the latest features.

# Install lintr from CRAN
install.packages("lintr")

# Or install the development version from GitHub
remotes::install_github("r-lib/lintr")

# Install styler from CRAN
install.packages("styler")

# Or install the development version from GitHub
remotes::install_github("r-lib/styler")

lintr requires R version 3.5.0 or later, while styler requires R 3.6.0 or later. Most modern R installations will satisfy these requirements.

Basic Linting with lint()

The lint() function analyzes a single R file and reports any style violations or potential issues. This is the simplest way to check your code for problems.

library(lintr)

# Lint a single file
lint("R/utils.R")

When you run this, lintr parses your code and returns a list of lint objects describing each issue. Each lint includes the file path, line number, column number, and a description of the problem.

For example, if your code has lines longer than 80 characters, you might see output like this:

# Example output for a file with long lines
lint("R/utils.R")
# <text:3> line too long (85 > 80): x <- "This is a very long line that exceeds the recommended maximum length of eighty characters"
# <text:10> missing spacing around operator: x<-1

You can also lint code stored as a character vector using lint_text():

code <- "x<-function(a){a+1}"
lint(text = code)

This is useful for quickly testing snippets without creating files.

Linting Entire Packages

When developing an R package, you’ll want to check all source files at once. The lint_package() function lints all R files in standard package locations: R/, tests/, inst/, vignettes/, and data-raw/.

library(lintr)

# Lint the current package
lint_package(".")

# Lint a specific package directory
lint_package("path/to/your/package")

You can also use lint_dir() to lint all R files in any directory, not just packages:

# Lint all R files in a directory recursively
lint_dir("R/", recursive = TRUE)

The lint_dir() function accepts a pattern argument to filter files. The default pattern "\\.[Rr]$" matches files ending in .R or .r.

Basic Styling with style_text()

The styler package formats R code automatically. The style_text() function takes a character vector of code and returns a styled version.

library(styler)

# Style a simple expression
code <- "x<-function(a,b){a+b}"
style_text(code)
# [1] "x <- function(a, b) {\n  a + b\n}"

Notice how styler added spaces around the assignment operator and between function arguments. The output follows the tidyverse style guide by default.

You can control the styling behavior with several parameters. The strict argument determines how strictly rules are applied:

# Strict mode (default) - applies all rules
style_text("x   <-   1", strict = TRUE)
# [1] "x <- 1"

# Non-strict mode - allows some flexibility
style_text("x   <-   1", strict = FALSE)
# [1] "x <- 1"

The scope parameter controls how invasive the transformations are. Options include "spaces", "indentation", "line_breaks", and "tokens". Higher scopes include all transformations from lower scopes.

Styling Entire Packages

Similar to lintr, styler can process entire packages with style_pkg(). This styles all R source files in a package while respecting the package structure.

library(styler)

# Style the current package
style_pkg(".")

# Style without modifying files (preview only)
style_pkg(".", dry = "on")

The dry parameter is particularly useful. Setting it to "on" shows you what would change without actually modifying your files. This lets you review the transformations before committing.

For non-package directories, use style_dir():

# Style all R files in a directory
style_dir("R/", recursive = TRUE)

Configuration via .lintr File

lintr reads configuration from a .lintr file in your project root. This file uses the Debian Control Format (DCF) and lets you customize which linters to use and which files to exclude.

Create a .lintr file in your project root:

linters: linters_with_defaults(
  line_length_linter(120),
  object_name_linter(styles = c("snake_case", "symbols")),
  commented_code_linter = NULL
)

exclusions: list(
  "inst/example/bad.R",
  "tests/testthat/exclusions-test" = 1
)

encoding: UTF-8

This configuration sets the maximum line length to 120 characters, configures object naming to allow snake_case and symbols, disables the commented code linter, and excludes specific files or lines from linting.

To exclude specific lines within a file, add a comment with # nolint:

# This line is excluded from linting # nolint
x <- 1  # nolint: line_length_linter

For specific linters, use the format # nolint:linter_name:

x<-1  # nolint:assignment_linter

You can also configure lintr programmatically using options():

options(lintr.linter_file = ".lintr")
options(lintr.cache_directory = "~/.cache/R/lintr")

styler doesn’t use a config file, but you can customize behavior through function arguments and by creating custom transformer functions.

Common Pitfalls to Avoid

Exclusion syntax errors: When excluding lint warnings, use # nolint not # nolint:. For specific linters, the format is # nolint:linter_name with an underscore, not a colon after “nolint”.

Cache corruption: If lintr results seem wrong, clear the cache with lintr::clear_cache(). This resolves many intermittent issues.

Preview before styling with styler: Always use dry = "on" first to see what will change. Styling modifies your files, and it’s easy to make unintended changes.

Scope confusion with styler: The "tokens" scope changes assignment operators from = to <-. Make sure you understand what each scope level does before applying it to a large codebase.

Object usage linter requirements: The object_usage_linter() requires your package to be loaded. Use pkgload::load_all() or ensure the package is installed before running this linter.

Encoding issues: If your files use non-UTF-8 encoding, specify it in your .lintr file. Otherwise, lintr may misparse your code and report false positives.


Both lintr and styler integrate well with continuous integration systems. You can add them to your GitHub Actions workflow to automatically check code quality on every push. This ensures that all contributors follow your project’s style conventions.

See Also: building-r-packages, r-debugging, r-renv