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.