rguides

Parameterised Reports with Quarto

Parameterised reports transform a single Quarto document into a flexible template. Instead of creating separate reports for each region, time period, or category, you define parameters once and let Quarto do the rest. This approach saves time, reduces errors, and makes your reporting workflow scalable.

What you’ll learn

This tutorial covers the key concepts and practical techniques for working with Parameterised Reports with Quarto. By the end, you will know how to apply the core functions in real data analysis workflows.

When to use parameters

Parameters shine in recurring reporting scenarios. Monthly sales reports that filter by different regions benefit from parameters. Quarterly analyses that compare different time periods become trivial. Ad-hoc reports that need customization without code changes are perfect candidates.

You should use parameters when:

  • You generate similar reports repeatedly with different filters
  • Different stakeholders need tailored versions of the same analysis
  • You want one source of truth that adapts to inputs
  • Your report logic stays the same but the data changes

If your report changes significantly between versions, separate documents make more sense. Parameters work best when the structure remains constant.

Defining parameters in YAML

Parameters live in the YAML header under the “parameters” key. Each parameter has a name and a default value. Quarto passes these to your R code as a list called “params”.

---
title: Regional Sales Report
parameters:
  region: "North"
  year: 2024
  target: 100000
---

This defines three parameters: “region” (a string), “year” (an integer), and “target” (a number). You can use any valid YAML value: strings, numbers, booleans, and even lists.

Using parameters in R code

Access parameters in your R code through the “params” object. This is a named list where each element corresponds to a parameter you defined.

# Access a single parameter
current_region <- params$region

# Use in your analysis
cat("Generating report for", params$region, "in year", params$year)

The real power emerges when you use parameters to filter data or control plot elements:

library(dplyr)
library(ggplot2)

# Filter data based on parameters
sales_data <- sales %>%
  filter(region == params$region, year == params$year)

# Create a plot with parameter-driven title
ggplot(sales_data, aes(x = month, y = sales)) +
  geom_line() +
  labs(title = paste("Sales in", params$region, "-", params$year))

You can also use parameters to conditionally include or exclude sections of your report:

# Conditionally render a section
if (params$include_forecasts) {
  # Generate forecast section
  print("Forecast section would be generated here")
}

Rendering with different parameters

Quarto provides two methods for rendering with custom parameter values.

Command line flags

The “-P” flag passes individual parameters from the command line:

quarto render report.qmd -P region:South

This overrides only the specified parameters. Others use their defaults from the YAML.

YAML parameter files

For complex parameter combinations, create a separate YAML file:

# params/northeast.yaml
region: "Northeast"
year: 2024
target: 150000

Render with the “—params” flag:

quarto render report.qmd --params-file params/northeast.yaml

This approach works well for batch processing. You can loop through multiple parameter files to generate all your reports at once.

Practical example: regional sales report

Lets build a complete example that ties everything together:

---
title: Regional Sales Report
parameters:
  region: "West"
  year: 2024
  target: 75000
---
#| include: false
library(dplyr)
library(ggplot2)

# Sample data (replace with your actual data source)
set.seed(123)
sales <- data.frame(
  year = rep(2021:2022, each = 6),
  month = month.abb,
  sales = runif(12, 5000, 15000) * (1 + params$year - 2020)
)

Filtering data with parameters

# Filter for the specified region and year
regional_sales <- sales %>%
  filter(year == params$year, region == params$region) %>%
  mutate(target = params$target)

regional_sales

Creating parameterised visualisations

ggplot(regional_sales, aes(x = month, y = sales, fill = sales > params$target)) +
  geom_col() +
  scale_fill_manual(values = c("FALSE" = "gray", "TRUE" = "green")) +
  labs(
    title = paste("Monthly Sales -", params$region, "Region"),
    subtitle = paste("Target:", scales::comma(params$target)),
    x = "Month",
    y = "Sales (USD)"
  ) +
  theme_minimal()

To generate this report for different regions, run:

# Generate for West (default)
quarto render sales-report.qmd

# Generate for East
quarto render sales-report.qmd -P region:East

# Generate for North
quarto render sales-report.qmd -P region:North

# Generate for South
quarto render sales-report.qmd -P region:South

Best practices

Name parameters clearly. Use descriptive names that indicate purpose. “include_forecasts” is better than “if”. Document what each parameter does in a comment.

Provide sensible defaults. Your document should work immediately when rendered without parameters. This makes testing easier and provides a fallback.

Validate parameter values. Check that parameters are within expected ranges or match valid options. This prevents silent failures:

valid_regions <- c("North", "South", "East", "West")
if (!(params$region %in% valid_regions)) {
  stop("Invalid region: ", params$region, 
       ". Must be one of: ", paste(valid_regions, collapse = ", "))
}

Use type hints in comments. Since YAML does not enforce types, add comments reminding yourself what type each parameter expects:

parameters:
  region: "West"     # string: region name
  year: 2024         # integer: 4-digit year
  target: 75000      # number: sales target

Keep parameters and logic separate. Define all parameter values in the YAML or external files. Keep your R code focused on analysis, not parameter management.

Test with multiple values. Before deploying a parameterised report, render it at least three times with different parameter combinations. This catches edge cases and ensures flexibility.

Parameterised reports are a gateway to automated reporting workflows. Once you master them, you can build pipelines that generate hundreds of customized reports from a single Quarto document.

Defining parameters

Parameters are declared in the YAML header with default values:

---
params:
  year: 2025
  region: "Global"
  min_revenue: 10000
---

Access parameters in code with params$year. Types are inferred from the YAML — numbers, strings, and booleans are supported. Complex structures (lists, vectors) can be passed as YAML sequences.

Rendering with parameters

quarto::quarto_render("report.qmd", execute_params = list(year = 2024, region = "EMEA")) renders with specific parameter values. For batch rendering across many parameter combinations, use purrr::walk() to iterate: walk(regions, ~ quarto_render("report.qmd", execute_params = list(region = .x), output_file = paste0(.x, "_report.html"))).

Dynamic content

Parameters enable conditionally including sections, changing titles, and filtering data. if (params$region == "EMEA") { ... } includes region-specific content. glue::glue("Sales Report: {params$region} {params$year}") builds dynamic titles. dplyr::filter(data, year == params$year) filters the data to the requested period.

Shiny runtime for interactive parameters

Adding server: shiny and params to a Quarto dashboard creates a parameter UI automatically. Users adjust parameters through the sidebar and the report regenerates. For simple parameter selectors without a full dashboard, runtime: shiny_prerendered precompiles the static parts and only re-runs the parameterized sections when inputs change.

Parameterization patterns

Quarto parameterization enables a single .qmd file to produce multiple distinct reports. Parameters can be strings, numbers, vectors, or any R-serializable object. The YAML params key defines them with default values.

For organizational reporting, the same template generates reports for each region, department, or product. For time series, the same template generates monthly, quarterly, or annual reports. For academic research, the same analysis template runs on different datasets.

Complex parameter objects: params: data_path: "data/q1.csv" passes a file path; the document loads that file. params: group_ids: [1, 3, 5] passes a vector (in YAML, this is a list). Access in R as params$group_ids, which arrives as a character vector; coerce with as.integer(params$group_ids).

Rendering parameterized reports from R

quarto::quarto_render("report.qmd", execute_params = list(region = "South")) renders the report with specified parameters. This is the R API equivalent of the CLI quarto render --p flag.

Batch rendering: generate one report per value in a parameter set. purrr::walk(regions, function(r) { quarto::quarto_render("report.qmd", execute_params = list(region = r), output_file = paste0("report-", r, ".html")) }).

For report generation pipelines, use targets to track which reports are current. A target for each region’s report, with the source data as an upstream target, rebuilds only reports affected by data changes.

Dynamic titles and filenames

title: "Report for r params$region" uses inline R in the YAML title. The backtick expression evaluates at render time, producing “Report for South” when rendered with region = "South". The same pattern works for subtitle: and date:.

Output file naming: the CLI --output flag or execute_params$output_file in the R API controls the filename. A common convention: output_file = paste0(tolower(params$region), "-", params$year, "-report.html") produces “south-2024-report.html”.

Parameter types and validation

Always validate parameters at the top of the document with an invisible setup chunk. Check that the parameter value is valid before the analysis runs:

#| include: false
stopifnot(
  params$region %in% c("North", "South", "East", "West"),
  params$year >= 2020 && params$year <= 2030
)

An informative error at the start is better than a confusing error 50 lines into the rendering. For enumerated parameters, match.arg(params$method, c("linear", "log", "sqrt")) provides helpful error messages listing valid choices.

Conditional content

Sections can be conditionally included based on parameters. In Quarto: if: params$include_appendix in a div block condition, or use knitr::knit_child() with eval controlled by a parameter.

In code chunks: #| eval: !expr params$skip_section evaluates the expression to determine whether to run the chunk. #| include: !expr params$show_details hides chunk output for summary-level reports.

Conditional headings in prose: wrap sections in if (params$region == "North") knitr::asis_output("# Northern Region Analysis") to include section headings only when relevant.

Parameterization as a reporting design pattern

Parameterized reports separate the template from the data. The template defines the analysis structure, visualizations, and prose that is consistent across all versions. The parameters define what varies: the region, the time period, the customer segment, the comparison benchmark. Changing a parameter produces a different version of the same report. This separation of concerns is what makes parameterized reports scalable — one template can produce hundreds of report variants.

The alternative to parameterization is copying the report template for each variant and modifying each copy manually. This approach does not scale and creates maintenance problems: when the template needs to change, you must update every copy. Parameterization solves both problems by keeping the template in one place and controlling variation through parameter values.

Parameter types and constraints

Quarto parameters can be strings, numbers, logical values, or lists. The parameter declaration in the YAML header specifies the default value. The type is inferred from the default value’s type. Adding input validation at the top of the document — checking that required parameters are not missing, that numeric parameters are in valid ranges, that string parameters are from an expected set — catches errors early with useful messages rather than letting invalid parameters cause confusing failures partway through rendering.

For reports with many parameters, a parameter validation section at the top that checks all constraints before executing any analysis code is good practice. A report that fails on page 15 because a parameter was invalid wastes more time than one that fails immediately with a clear message identifying the problematic parameter and what value it received.

Batch rendering patterns

Rendering multiple report variants in a loop uses rmarkdown::render or quarto_render with different parameter lists. Constructing the output file name from the parameter values keeps the output files organized and identifiable. For a regional report with parameters for region and year, the output file name includes both, making it clear which report covers which scope.

Parallel batch rendering speeds up large report runs. The future package with future_map from furrr distributes render calls across multiple cores. Each render call runs independently, so parallelism is safe. For very large batches on a server with many cores, parallel rendering is the difference between a two-hour batch run and a fifteen-minute one.

Next steps

Now that you understand parameterised reports with quarto, explore these related topics to deepen your knowledge and apply these techniques in more complex scenarios.