CLI Inline Markup Reference
Table of Contents
- Introduction
- Basic Syntax
- Code & Syntax Classes
- Files & Paths Classes
- Communication Classes
- Values & Data Classes
- Emphasis Classes
- Documentation Classes
- Special Classes
- Vector Collapsing
- Advanced Patterns
- Pluralization
- Performance Considerations
- Quick Reference Table
Introduction
Read this file when you need to:
- Look up the correct inline markup class for specific content types
- Understand how to nest and combine markup classes
- Learn vector collapsing behavior and customization
- Master pluralization patterns beyond basic
{?s} - Optimize performance for high-frequency messaging
- Troubleshoot unexpected formatting behavior
Inline markup classes format text within cli messages using {.class content} syntax. They integrate with glue string interpolation and work across all cli functions: cli_text(), cli_abort(), cli_warn(), cli_inform(), cli_alert_*(), etc.
Basic Syntax
Inline markup uses curly braces with a period-prefixed class name:
cli_text("Function {.fn mean} calculates {.field average}")
#> Function `mean()` calculates average
Key syntax rules:
- Format:
{.class content} - Content is interpolated if it contains
{} - Double braces
{{and}}escape literal braces - Whitespace inside
{}is preserved in output - Classes can be nested (see Advanced Patterns)
Code & Syntax Classes
.code
Generic code formatting for expressions, statements, or syntax:
cli_text("Use {.code sum(x, na.rm = TRUE)} to ignore NA values")
cli_text("Set {.code options(width = 120)} in your .Rprofile")
cli_text("The {.code return()} statement exits early")
When to use:
- Multi-token code expressions
- Syntax examples
- Configuration snippets
- When other code classes are too specific
.fn and .fun
Function names with automatic parentheses:
cli_text("Call {.fn mean} to calculate average")
#> Call `mean()` to calculate average
cli_text("Use {.fun base::mean} for namespace clarity")
#> Use `base::mean()` for namespace clarity
Automatic formatting:
- Adds
()suffix automatically - Supports
package::functionnotation - Both
.fnand.funare equivalent
When to use:
- Referring to functions by name
- Suggesting which function to call
- Error messages about function usage
.arg
Function argument names:
cli_abort(c(
"{.arg x} must be numeric",
"i" = "Set {.arg na.rm = TRUE} to handle missing values"
))
When to use:
- Parameter validation errors
- Documenting function arguments
- Explaining argument behavior
.cls
S3/S4/R6 class names:
cli_abort("Expected {.cls data.frame}, got {.cls {class(x)}}")
cli_text("Object is {.cls tbl_df}, a tibble subclass")
When to use:
- Type checking errors
- Documenting expected types
- Explaining inheritance
.type
Broader type descriptions (base types, generic categories):
cli_abort("Input must be {.type integer}, not {.type {typeof(x)}}")
cli_text("Coercing from {.type character} to {.type numeric}")
When to use:
- Base R types: integer, double, character, logical
- Generic type categories
- Type coercion messages
.obj_type_friendly
User-friendly type descriptions with indefinite articles:
x <- data.frame()
cli_text("{.var x} must be a vector, not {.obj_type_friendly {x}}")
#> `x` must be a vector, not a data frame
y <- 1:10
cli_text("You provided {.obj_type_friendly {y}}")
#> You provided an integer vector
Automatic features:
- Adds "a"/"an" article automatically
- Uses friendly names ("data frame" not "data.frame")
- Handles pluralization for vectors
Files & Paths Classes
.file
File names and paths with appropriate formatting:
cli_text("Reading {.file data/input.csv}")
cli_warn("File {.file ~/.ssh/config} has insecure permissions")
cli_inform("Created {.file output/results.xlsx}")
Best practices:
- Use for any file reference
- Works with relative and absolute paths
- Handles home directory expansion
.path
Directory paths and file system locations:
cli_text("Installing to {.path /usr/local/lib/R}")
cli_text("Working directory: {.path {getwd()}}")
cli_abort("Directory {.path {dir}} does not exist")
When to use .file vs .path:
.file- Files, scripts, documents.path- Directories, installation locations, system paths
Communication Classes
Email addresses:
cli_text("Contact maintainer at {.email user@example.com}")
cli_inform("Send bug reports to {.email bugs@r-project.org}")
Features:
- Creates
mailto:links in supported terminals - Formatted distinctly from regular text
.url
Web URLs and URIs:
cli_text("Visit {.url https://cli.r-lib.org} for documentation")
cli_text("API endpoint: {.url https://api.example.com/v1}")
Features:
- Creates clickable hyperlinks in supported terminals
- Formats protocol, domain, and path distinctly
.href
Custom hyperlinks with separate text and URL:
cli_text("See {.href [documentation](https://cli.r-lib.org)}")
cli_text("Read {.href [vignette](vignette:cli::inline-markup)}")
Syntax:
- Format:
{.href [text](url)} - Markdown-style link syntax
- Text and URL can be styled differently
Link types:
- HTTP/HTTPS:
https://example.com - Help topics:
help:topic - Vignettes:
vignette:package::topic
Values & Data Classes
.val
Data values, constants, and literals:
n <- 42
cli_text("Found {.val {n}} records")
cli_text("Default timeout is {.val 30} seconds")
cli_text("Status: {.val 'complete'}")
Automatic formatting:
- Quoted for character values
- Unquoted for numeric values
- Handles vectors with collapsing
.var
Variable names in code or data:
cli_abort("Variable {.var x} must be numeric")
cli_text("Column {.var age} contains missing values")
cli_inform("Using {.var Sepal.Length} as predictor")
When to use:
- Variable names in R code
- Column names in data frames
- Field names in objects
.envvar
Environment variable names:
cli_text("Set {.envvar R_LIBS_USER} to customize library location")
cli_abort("{.envvar HOME} is not defined")
cli_inform("Using {.envvar PATH}: {.val {Sys.getenv('PATH')}}")
When to use:
- System environment variables
- R-specific environment variables
- Configuration via environment
.field
Object fields, slots, or attributes:
cli_text("The {.field name} field is required")
cli_abort("Invalid {.field status} value")
cli_text("Access {.field @data} slot in S4 object")
When to use:
- Named list elements
- Data frame columns (prefer
.varfor analysis context) - S4 slots
- Object attributes
.str
String literals and text values:
cli_text("Message starts with {.str 'Error:'}")
cli_text("Pattern {.str '^[0-9]+$'} matches digits")
When to use:
- Literal string values
- Pattern strings
- Format strings
- When
.valformatting is too generic
Emphasis Classes
.emph
Emphasis for important concepts or terms:
cli_text("This function is {.emph deprecated}")
cli_text("The file is {.emph locked} by another process")
cli_inform("{.emph Note}: This may take several minutes")
Rendering:
- Typically italic or colored
- Lighter emphasis than
.strong
.strong
Strong emphasis for critical information:
cli_warn("{.strong Warning}: This action cannot be undone")
cli_text("This parameter is {.strong required}")
cli_abort("{.strong Error}: Database connection failed")
Rendering:
- Typically bold or brightly colored
- Stronger emphasis than
.emph
Documentation Classes
.help
R help topic references:
cli_text("See {.help stats::lm} for details")
cli_inform("More info: {.help base::sum}")
Features:
- Creates link to help topic in RStudio
- Supports
package::topicnotation - Fallback to plain text in terminals
.topic
Generic topic or section references:
cli_text("See {.topic 'Error Handling'} section")
cli_inform("Refer to {.topic 'Advanced Usage'}")
When to use:
- Internal documentation sections
- Vignette sections
- General topic references
.vignette
Vignette references:
cli_text("Read {.vignette cli::semantic-cli} for examples")
cli_inform("See {.vignette dplyr::programming}")
Features:
- Links to package vignettes
- Format:
package::vignette-name
.run
Runnable R code examples:
cli_text("Try: {.run install.packages('cli')}")
cli_inform("Debug with: {.run options(error = recover)}")
Features:
- Creates executable link in RStudio
- Click to run code in console
- Formatted as code in terminals
Special Classes
.kbd
Keyboard keys and shortcuts:
cli_text("Press {.kbd Ctrl+C} to cancel")
cli_text("Use {.kbd Enter} to confirm")
cli_inform("Save with {.kbd Cmd+S} (Mac) or {.kbd Ctrl+S} (Windows)")
Rendering:
- Typically in keyboard key style
- May show as boxed or distinct formatting
.key
Alternative to .kbd for key names:
cli_text("Press the {.key RETURN} key")
cli_text("Hold {.key SHIFT} while clicking")
.or
Logical OR separator for alternatives:
cli_text("Use {.val 'yes'} {.or} {.val 'no'}")
cli_abort("Type must be {.val 'auto'} {.or} {.val 'manual'}")
Rendering:
- Formats as " or " with appropriate styling
- Maintains class formatting for surrounding elements
.pkg
Package names:
cli_text("Install {.pkg dplyr} for data manipulation")
cli_inform("Loading {.pkg ggplot2}")
cli_abort("{.pkg httr2} is required but not installed")
Features:
- Distinct package name formatting
- May include CRAN/GitHub links in supported environments
.dt
Definition term in definition lists:
cli_dl(c(
"{.dt name}" = "User's full name",
"{.dt email}" = "Contact email address"
))
When to use:
- Definition list terms
- Glossary entries
- Key-value pair keys
.dd
Definition description in definition lists:
cli_dl(c(
"name" = "{.dd User's full name}",
"email" = "{.dd Contact email address}"
))
When to use:
- Definition list descriptions
- Glossary definitions
- Key-value pair values
Vector Collapsing
Vectors are automatically collapsed with appropriate separators:
Default Behavior
pkgs <- c("dplyr", "tidyr", "ggplot2")
cli_text("Loading packages: {.pkg {pkgs}}")
#> Loading packages: dplyr, tidyr, and ggplot2
files <- c("a.R", "b.R")
cli_text("Modified: {.file {files}}")
#> Modified: a.R and b.R
single <- "data.csv"
cli_text("Found: {.file {single}}")
#> Found: data.csv
Rules:
- Length 1: no separators
- Length 2: " and " separator
- Length 3+: ", " separators with " and " before last item
Collapsing Empty Vectors
empty <- character()
cli_text("Files: {.file {empty}}")
#> Files:
cli_text("Files: {?none/one/some}: {.file {empty}}")
#> Files: none:
Custom Collapse Separators
Control collapsing with glue transformers:
items <- c("apple", "banana", "cherry")
# Custom separator
cli_text("Items: {.val {items}}", .transformer = function(text, envir) {
glue::glue_collapse(text, sep = " | ", last = " | ")
})
#> Items: 'apple' | 'banana' | 'cherry'
# OR separator
cli_text("Choose: {.val {items}}", .transformer = function(text, envir) {
glue::glue_collapse(text, sep = ", ", last = " or ")
})
#> Choose: 'apple', 'banana' or 'cherry'
Truncating Long Vectors
many <- letters[1:20]
cli_text("Variables: {.var {many}}")
#> Variables: a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, and t
# Truncate with custom transformer
cli_text("Variables: {.var {head(many, 5)}} and {length(many) - 5} more")
#> Variables: a, b, c, d, e and 15 more
Advanced Patterns
Nested Markup
Classes can be nested for combined formatting:
cli_text("Call {.fn {.pkg dplyr}::filter} to subset data")
cli_abort("File {.file {.path {dir}}/data.csv} not found")
cli_text("Set {.arg timeout} to {.val {default_timeout}} by default")
Nesting guidelines:
- Outer class determines primary formatting
- Inner classes add semantic meaning
- Keep nesting depth reasonable (2-3 levels max)
Combining with Glue Expressions
n <- 5
cli_text("Processing {n} file{?s} from {.path {getwd()}}")
status <- "complete"
cli_inform("Status: {.val {toupper(status)}}")
pkg <- "dplyr"
cli_text("Version: {.pkg {pkg}} {.val {packageVersion(pkg)}}")
Custom Glue Transformers
Full control over formatting with transformers:
# Highlight differences
cli_text(
"Expected {.val {expected}}, got {.val {actual}}",
.transformer = function(text, envir) {
# Custom logic here
text
}
)
# Add prefixes
my_transformer <- function(text, envir) {
if (startsWith(text, ".pkg")) {
paste0("R package: ", glue::glue(text, .envir = envir))
} else {
glue::identity_transformer(text, envir)
}
}
cli_text("Install {.pkg cli}", .transformer = my_transformer)
Conditional Formatting
status <- "error"
cli_text("Status: {.{if(status == 'error') 'strong' else 'emph'} {status}}")
type <- class(x)
cli_text("Type: {if(is.numeric(x)) .val else .cls} {type}")
Multiple Classes on Same Content
Combine semantic classes:
# Package function
cli_text("{.fn {.pkg stats}::median}")
# File in path
cli_text("{.file {.path /usr/local}/script.R}")
# Variable value
cli_text("{.var x} = {.val {x}}")
Working with Lists
config <- list(host = "localhost", port = 8080, ssl = TRUE)
cli_dl(c(
"{.field host}" = "{.val {config$host}}",
"{.field port}" = "{.val {config$port}}",
"{.field ssl}" = "{.val {config$ssl}}"
))
Escaping Markup
Prevent interpretation with double braces:
cli_text("In glue, use {{variable}} syntax")
#> In glue, use {variable} syntax
cli_text("Literal: {{.code not_markup}}")
#> Literal: {.code not_markup}
Pluralization
Pluralization adapts text based on quantities using {?} syntax.
Single Alternative Pattern
Add "s" for plural:
n <- 1
cli_text("{n} file{?s} found")
#> 1 file found
n <- 5
cli_text("{n} file{?s} found")
#> 5 files found
Two Alternative Pattern
Specify singular/plural forms:
n <- 1
cli_text("{n} director{?y/ies}")
#> 1 directory
n <- 3
cli_text("{n} director{?y/ies}")
#> 3 directories
Common patterns:
{?y/ies}- directory/directories{?/s}- item/items{?is/are}- is/are{?/es}- box/boxes{?ex/ices}- index/indices
Three Alternative Pattern
Handle zero/one/many:
n <- 0
cli_text("{?No/One/Some} file{?s} {?is/is/are} ready")
#> No files are ready
n <- 1
cli_text("{?No/One/Some} file{?s} {?is/is/are} ready")
#> One file is ready
n <- 5
cli_text("{?No/One/Some} file{?s} {?is/is/are} ready")
#> Some files are ready
Setting Quantity with qty()
Control pluralization explicitly:
updated <- 3
total <- 10
cli_text("{updated}/{total} {qty(updated)} file{?s} {?needs/need} update{?s}")
#> 3/10 files need updates
When to use qty():
- When quantity appears elsewhere
- Complex expressions with multiple numbers
- Explicit pluralization control
no() Helper
Display "no" instead of 0:
n <- 0
cli_text("Found {no(n)} error{?s}")
#> Found no errors
n <- 3
cli_text("Found {no(n)} error{?s}")
#> Found 3 errors
Advanced Pluralization Patterns
Multiple quantities in one message:
nerr <- 2
nwarn <- 1
cli_text(
"{nerr} error{?s} and {nwarn} {qty(nwarn)} warning{?s} found"
)
#> 2 errors and 1 warning found
Conditional articles:
n <- 1
cli_text("Found {?a /}{n} file{?s}")
#> Found a 1 file
n <- 5
cli_text("Found {?a /}{n} file{?s}")
#> Found 5 files
Complex pluralization:
nfile <- 3
ndir <- 1
cli_text(
"{nfile} file{?s} in {ndir} {qty(ndir)} director{?y/ies}"
)
#> 3 files in 1 directory
Verb agreement:
n <- 1
cli_text("File {?was/were} modified")
#> File was modified
n <- 3
cli_text("Files {?was/were} modified")
#> Files were modified
Possessives:
n <- 1
cli_text("User{?'s/'s'} setting{?s}")
#> User's setting
n <- 3
cli_text("Users{?'s/'s'} settings")
#> Users' settings
Performance Considerations
High-Frequency Messages
For loops with many iterations:
# Expensive - creates cli context each iteration
for (i in 1:10000) {
cli_text("Processing {.val {i}}")
}
# Better - batch messages
if (i %% 1000 == 0) {
cli_text("Processed {.val {i}} items")
}
# Best - use progress bar
cli_progress_bar("Processing", total = 10000)
for (i in 1:10000) {
# work here
cli_progress_update()
}
Expensive Interpolation
Avoid expensive computations in markup:
# Expensive - computes every time
cli_text("Processing {.file {slow_path_computation()}}")
# Better - compute once
path <- slow_path_computation()
cli_text("Processing {.file {path}}")
Conditional Messages
Use conditions to avoid unnecessary formatting:
# Inefficient
if (verbose) cli_text("Status: {.val {expensive_status_check()}}")
# Better
if (verbose) {
status <- expensive_status_check()
cli_text("Status: {.val {status}}")
}
Large Vectors
Truncate large vectors before formatting:
# Problematic with huge vectors
vars <- names(huge_dataframe)
cli_text("Variables: {.var {vars}}")
# Better
n_vars <- length(vars)
if (n_vars > 10) {
cli_text("Variables: {.var {head(vars, 10)}} and {n_vars - 10} more")
} else {
cli_text("Variables: {.var {vars}}")
}
Quick Reference Table
| Class | Use For | Example | Output |
|---|---|---|---|
.code |
Code expressions | {.code sum(x)} |
sum(x) |
.fn, .fun |
Functions | {.fn mean} |
mean() |
.arg |
Function arguments | {.arg na.rm} |
na.rm |
.cls |
Class names | {.cls data.frame} |
data.frame |
.type |
Base types | {.type integer} |
integer |
.obj_type_friendly |
Friendly types | {.obj_type_friendly {x}} |
an integer vector |
.file |
File names | {.file script.R} |
script.R |
.path |
Directory paths | {.path /usr/local} |
/usr/local |
.email |
Email addresses | {.email user@example.com} |
user@example.com |
.url |
Web URLs | {.url https://example.com} |
https://example.com |
.href |
Custom links | {.href [text](url)} |
text (linked) |
.val |
Data values | {.val {x}} |
42 or 'text' |
.var |
Variable names | {.var column} |
column |
.envvar |
Environment vars | {.envvar PATH} |
PATH |
.field |
Object fields | {.field name} |
name |
.str |
String literals | {.str 'pattern'} |
'pattern' |
.emph |
Emphasis | {.emph important} |
important |
.strong |
Strong emphasis | {.strong critical} |
critical |
.help |
Help topics | {.help stats::lm} |
stats::lm (linked) |
.topic |
Topic references | {.topic 'Intro'} |
'Intro' |
.vignette |
Vignettes | {.vignette pkg::name} |
pkg::name (linked) |
.run |
Runnable code | {.run code} |
code (executable) |
.kbd, .key |
Keyboard keys | {.kbd Ctrl+C} |
Ctrl+C |
.or |
Logical OR | {.val x} {.or} {.val y} |
x or y |
.pkg |
Package names | {.pkg dplyr} |
dplyr |
.dt |
Definition term | {.dt term} |
term (in definition list) |
.dd |
Definition desc | {.dd description} |
description (in definition list) |