Quarto Documents in R: Structure and Rendering
What a Quarto document is
A Quarto document is a plain-text file with the extension .qmd that mixes prose, executable code, and metadata. Under the hood, Quarto runs the R chunks through Knitr and hands the result to Pandoc for conversion to your output format. That is the same pipeline R Markdown uses, with a few additions: a unified YAML schema, a new cell-option syntax, and first-class support for languages other than R.
Quarto itself is a separate command-line tool, not an R package. You install the Quarto CLI once on your machine, and R finds it through your PATH. The R package called quarto is a thin wrapper that calls the CLI for you. If the CLI is missing, quarto::quarto_render() will fail too. Verify it works from a terminal first:
quarto --version
The shell should print a version string like 1.6.0 (the exact number does not matter, as long as something prints). If you get a “command not found” error, install the CLI and check that the install directory is on your PATH. From inside R, you can confirm the same thing with a one-liner that queries the CLI through the wrapper package:
library(quarto)
quarto_version() # confirm the CLI is reachable from R
The three parts of a .qmd file
Every Quarto document has three sections, in this order:
- YAML front matter, wrapped in
---, holds metadata such as the document title, its author, and the output format setting. - Markdown body, regular prose with headings, lists, links, math, and citations.
- Executable code cells, fenced blocks tagged with a language (here,
r) that Quarto runs and inlines into the document.
A minimum-viable file looks like this:
---
title: "My First Quarto Document"
format: html
---
That is the entire document. Save it as hello.qmd and you have a working Quarto file, although a boring one with no executable content. To make it run R, wrap your code in a fenced cell that Quarto recognises. The opening fence is three backticks followed by {r} (and any cell options), and the closing fence is just three backticks. Adding this cell below the YAML in the same file would evaluate 1 + 1 when you render:
```{r}
1 + 1
```
When you render the document with quarto render hello.qmd, Quarto replaces the cell with the printed result of evaluating that R expression. The two hash signs at the start of the output line are a knitr convention that makes results easier to tell apart from source code at a glance. The rendered output for the cell above looks like this:
## [1] 2
If you omit format: entirely, Quarto defaults to HTML, which is what you usually want when iterating locally.
Writing code cells in R
Code cells are fenced blocks whose opening fence ends with {r}. Inside the cell you write ordinary R. Knitr evaluates the cell top to bottom, just like a script, and the output is pasted into the rendered document in the same place.
Quarto extends the old knitr chunk-header style with a cleaner, comment-prefixed syntax. Options live in #| lines at the top of the cell. A figure cell with options and the ggplot code looks like this (the cell wrapper ``` {r} … ``` is implied around the snippet):
```{r}
#| label: fig-mpg
#| fig-cap: "Engine displacement vs. fuel economy."
#| fig-width: 6
#| fig-height: 4
#| echo: false
library(ggplot2)
ggplot(mtcars, aes(disp, mpg)) +
geom_point(color = "steelblue") +
geom_smooth(method = "lm", se = FALSE)
```
A label prefixed with fig-, tbl-, or lst- makes the cell cross-referenceable from prose. The old # echo = FALSE, fig.cap = "..." form still works, so existing .Rmd files render in Quarto without changes, but new .qmd files should use the #| form because it is the one Quarto documents in its own reference.
The most useful cell options, with their defaults, are:
| Option | What it does | Default |
|---|---|---|
eval | Run the code (true / false / numeric vector) | true |
echo | Show the source in the output | true for HTML |
output | Include the printed results | true |
include | Run, but hide both code and output | true |
warning | Show warnings | true |
message | Show package-load messages | true |
error | Show errors and keep rendering | false |
fig-width, fig-height | Plot size in inches | format-dependent |
fig-cap | Figure caption (requires a fig- label) | none |
The echo: false and include: false pair is worth memorising. echo: false hides the source but keeps the output, which is what you want for a final report. include: false runs the code and shows nothing, which is what you want for a setup chunk that loads packages.
Inline R code
For a single value embedded in a sentence, use a single backtick, the letter r, and an expression. The inline form is the same as a code cell, just with a single backtick on each side of the expression. The source for a sentence that reports the dimensions of a dataset looks like this (note the r immediately after the opening backtick):
The `mtcars` dataset has `r nrow(mtcars)` rows and `r ncol(mtcars)` columns.
Quarto evaluates that source at render time and replaces the inline expressions with their values. The rendered output reads as a plain sentence, with no surrounding backticks and no code-block formatting:
The mtcars dataset has 32 rows and 11 columns.
Inline code is read at render time, so the same document can show today’s date with `r Sys.Date()`. It also means a stale render can lie about your data. When in doubt, re-render before sharing.
Output formats
You set the output format in YAML. To produce more than one, list them under format::
---
title: "Cars Report"
author: "Jane Analyst"
date: "2026-06-06"
format:
html:
toc: true
code-fold: true
theme: cosmo
pdf:
documentclass: article
docx: default
---
A few things to know about each format:
- HTML is the default and the fastest to iterate on.
code-fold: trueputs each chunk in a collapsible<details>block;code-tools: trueadds a copy-button toolbar. - PDF goes through LaTeX, so you need a TeX distribution. The easiest path for R users is
tinytex::install_tinytex()from thetinytexpackage. Without it, you will get errors like! LaTeX Error: File 'whatever.sty' not found. - DOCX uses a Word reference template. Drop a customised
.docxin the project and point at it withreference-doc: custom.docxunderformat: docx:.
Rendering a document
There are three common ways to render a Quarto document, and they all do the same thing under the hood.
The RStudio Render button is the most common path. Click it (or use the keyboard shortcut Ctrl+Shift+K on Windows and Linux, or Cmd+Shift+K on macOS) and RStudio renders the first format listed in the YAML, or HTML if format: is missing.
From the shell, the target file is always the first argument to quarto render:
quarto render report.qmd # all formats in YAML
quarto render report.qmd --to pdf # one specific format
A subtle rule that catches people: quarto render --to pdf report.qmd does not work. The file must come first, the format flag after. Most Unix CLIs accept flags in any order, which is exactly what makes this confusing when you switch to Quarto. There is no shortcut for placing the format flag before the file path.
From R, quarto::quarto_render() shells out to the CLI, so you can render from a script or a function. The output_format argument is the R-side equivalent of the --to flag, and the file path is the first positional argument just like on the CLI:
library(quarto)
quarto_render("report.qmd", output_format = "pdf")
All three paths produce the same output. The CLI is the one with the most flags and is the easiest to call from a Makefile or CI workflow.
Cross-references and figures
Cross-references depend on a labelled cell and a @ reference in prose. A figure label is a label option prefixed with fig-, and the reference in prose is @ followed by that label with no space between them. Here is a complete example: a figure cell that produces a plot, followed by a Markdown sentence that points at that plot.
```{r}
#| label: fig-airquality
#| fig-cap: "Temperature and ozone level."
#| fig-alt: "Scatter plot of temperature and ozone from the airquality dataset."
library(ggplot2)
ggplot(airquality, aes(Temp, Ozone)) +
geom_point() +
geom_smooth(method = "loess")
```
See @fig-airquality for the relationship.
Quarto replaces @fig-airquality with a numbered link like “Figure 1” in the rendered output. The same pattern works for tables (tbl-) and code listings (lst-). Cell labels must be unique within a document.
Common mistakes
A few things that go wrong often enough to be worth flagging:
- Forgetting the CLI.
quarto::quarto_render()will fail with a “command not found” error if the Quarto CLI is not onPATH. Runquarto --versionin the same shell you launch R from. - PDF without LaTeX. Install TinyTeX with
tinytex::install_tinytex()before rendering to PDF for the first time. echo: falsevsinclude: false.echo: falsestill shows the plot;include: falsehides everything. Useinclude: falsefor setup chunks that load libraries.- Non-unique labels. Two cells with
label: fig-fooin the same document will fail to render or produce broken cross-references. - Inline
!exprin YAML. It is technically supported but brittle. Compute the value in a chunk and reference the variable instead. - Mixing
.qmdfeatures in.Rmd. Aformat:key or#|comment in an.Rmdfile is ignored byrmarkdown::render(). If you need Quarto features, save the file as.qmd.
Conclusion
A Quarto document is just three things in a row: YAML metadata, Markdown prose, and R code cells. Once you know the #|-prefixed option syntax and the three rendering paths (RStudio button, quarto render on the CLI, or quarto::quarto_render() in R), you can spin up HTML, PDF, and DOCX reports from the same source file.
For a broader overview that covers websites, books, and presentations in addition to documents, see the Quarto guide. For a step-by-step setup walkthrough, the Quarto getting started tutorial is the right place. If you are coming from R Markdown and want to see what changed, Quarto vs R Markdown covers the differences.