Reference
Every option the typst-render extension accepts.
Enabling the filter
filters:
- typst-renderReference it by name, with no at: stage. It registers itself at the several stages it needs, and pinning it to one breaks cross-referencing and the math: typst takeover.
Engines
A ```{typst} block looks like an executable cell to Quarto. The document engine sees it before the filter does, and not every engine leaves it alone.
| Engine | What happens | What you do |
|---|---|---|
markdown |
The block reaches the filter untouched. | Nothing, apart from the blank-line caveat below. Best for a document with no executable code, as example.qmd and these pages are. |
jupyter, julia |
The block passes through untouched. | Nothing, apart from the blank-line caveat below. |
knitr |
knitr claims the cell and knows no typst engine, so it produces no figure. |
Register a passthrough engine, as below. |
| none | Engine detection selects Jupyter, which then fails for want of a kernel. | Set engine: markdown, or name the engine you want. |
An inline `{typst}` expression is ordinary inline code, so every engine leaves it alone.
With the markdown, jupyter, and julia engines, avoid blank lines inside a {typst} block when you render to Typst or PDF. Quarto reads the page with the plain pandoc reader before those renders, and that reader misreads such a block. It prints a spurious Div … unclosed warning and can turn a level-one heading into plain text. The knitr engine below avoids this, as knitr explains.
knitr
knitr writes this warning:
Unknown language engine 'typst' (must be registered via knit_engines$set()).The block then reaches the page as highlighted Typst source, with no figure beside it.
Register a passthrough engine in a setup chunk, so that knitr hands the block back unchanged:
```{r}
#| include: false
knitr::knit_engines$set(typst = function(options) {
knitr::asis_output(paste0("\n```{.typst-render-cell}\n", paste(options[["code"]], collapse = "\n"), "\n```\n"))
})
```The engine writes the block back with the typst-render-cell class, which the filter reads as a {typst} block. A bare {typst} fence works for the render itself, but not for a check Quarto runs first when rendering to Typst or PDF. That check uses the plain pandoc reader, which doesn’t know {typst}. A blank line inside the block then makes it misread the rest of the page, so it prints a spurious Div … unclosed warning and can turn a level-one heading into plain text.
Per-block options, cross-reference labels, and captions all work after that, written as //| lines as in Per-block options. knitr removes #| lines and chunk header options before the engine runs, so the filter never sees them. The helper’s engine warns when it finds any; the snippet above does not. The R helper in Passing data from R or Python performs the same registration, so a document that calls typst_define() needs nothing more. Data from R and Python shows the whole setup.
Passing data from R or Python
typst_define() pushes named values from a knitr or jupyter session into the {typst} blocks of the document. Each value is read back as #typst_define.<name>.
Two helpers ship with the extension, under _extensions/mcanouil/typst-render/_resources/. There is no Julia helper, so this feature covers knitr and jupyter only.
The R helper needs rlang and jsonlite. Load it in a setup chunk:
```{r}
#| include: false
source("_extensions/mcanouil/typst-render/_resources/typst_define.R")
```The Python helper needs IPython. Load it the same way:
```{python}
#| include: false
import sys
sys.path.insert(0, "_extensions/mcanouil/typst-render/_resources")
from typst_define import typst_define
```Call it wherever the values are ready:
typst_define(n = 42L, d = data.frame(mpg = c(21, 22.8), cyl = c(6L, 4L)))The call emits a hidden metadata block, so it adds nothing to the page. The chunk source is still echoed under the usual echo and include options.
Read the values by name:
n is #typst_define.n, and the first mpg is #typst_define.d.mpg.at(0).Every {typst} block of the document reads the same values, wherever it sits. The engine runs the whole document before the filter does, so a block above the call sees them as well.
Scalars, strings, booleans, arrays, and nested objects are carried as written. An R, pandas, or polars data frame is carried column-wise, as a dictionary of arrays. A numpy array becomes a Typst array.
Names keep the order of the calls that defined them. Inside a carried object or data frame, however, the keys arrive sorted by name, so read a column by name and never by position. An R data frame with row names carries them under an added _row key.
Each call adds to the values already defined, and a repeated name takes the newer value. The payload travels as hex-encoded JSON, because Pandoc’s smart quotes and dashes would otherwise corrupt it.
Global options
Set under extensions.typst-render.
| Option | Default | Description |
|---|---|---|
format |
auto | png, svg, pdf, or html. html gives semantic markup with MathML maths, needs Typst 0.15, and falls back to SVG. |
math |
Set to typst to render every equation in the document as Typst maths rather than LaTeX. Quarto numbering and cross-references keep working. |
|
dpi |
144 |
Pixels per inch, for PNG. |
width, height |
auto |
Page size for compilation. Ignored with output: asis. |
margin |
0.5em |
Page margin, or block inset with output: asis. |
background |
none |
Page fill, or the block fill with output: asis. A Typst colour, auto to read _brand.yml, or a map with light and dark keys. |
foreground |
Text fill for the compiled image, taking the same three shapes. | |
preamble |
Typst code, or paths to .typ files, prepended to every block. |
|
root |
Root directory for compilation. Global only. | |
font-path, package-path |
Extra fonts, and a package directory for offline builds. Global only. | |
input |
Key-value pairs passed as --input, readable through sys.inputs. |
|
cache |
true |
Cache compiled images between renders. |
cache-refresh |
false |
Remove stale cache files after each render. |
output-directory |
./assets/typst-render |
Where saved images are written, relative to the document, under a subdirectory named after it. An empty string writes none, leaving them in the render cache where a deployed site cannot reach them. |
output-source |
false |
Also write the compiled Typst source beside each image. |
Maths shows the math: typst takeover, and Output and files shows preamble, input, output-directory, and the cache at work.
Per-block options
Written as //| key: value at the top of a block.
| Option | Default | Description |
|---|---|---|
format |
The output format for this block alone: png, svg, pdf, or html. |
|
echo |
false |
Show the source alongside the output. fenced includes the block markers. |
code-fold |
false |
Collapse the echoed source into a <details>. show renders it open. HTML only. |
code-summary |
Code |
The summary text for that <details>. |
code-line-numbers |
false |
Highlight lines of the echoed source. A string such as 1|3-4 steps through them in RevealJS. |
eval |
true |
Whether to compile at all. |
include |
true |
Whether to embed the result. false still compiles and writes files. |
output |
true |
Whether to show the result. asis passes the code through natively in Typst output. |
output-location |
RevealJS placement: fragment, slide, column, column-fragment. |
|
classes |
CSS classes for the image element, such as r-stretch. |
|
pages |
all |
Which pages of a multi-page result to include: 1, 1-3, 2,5, 3-. |
layout-ncol |
Arrange multi-page output in this many columns. | |
align |
left, center, right, or default. |
|
label, cap, alt |
Cross-reference label, caption, and alternative text. | |
file, output-filename |
Compile an external .typ file, and name the written image. |
The two tables are one set of options read at two levels. Everything in the first can be set on a block, apart from math, root, font-path, package-path, and cache-refresh, which are global only. A cache-refresh written on a block is dropped, with a warning. Everything in the second can be set globally as a default for every block, apart from file, output-filename, label, cap, and alt, which belong to a block alone.
A block option is one line, so an option that takes a map, such as a background with a light and a dark value, is written in YAML and never on a block.
A block without alt gets its alt text from the other options. A block with a label and a cap gets no alt text, because the caption already describes the figure, as for Quarto figures. A block with a cap but no label uses the caption as plain text, without markdown. A block with no caption uses the Typst source.
Figures and tables shows the size, format, and alignment options, Cross-references shows label, cap, and alt, and Presentations shows the Reveal.js options.
Inline expressions
A red word `{typst} #text(red)[hello]` in a sentence.An inline expression takes the global options that shape the compilation, format, dpi, background, foreground, preamble, input, cache, output, eval, include, classes, output-directory, and output-source among them. Its image is written as typst-inline-<N>.<ext>, numbered in document order, beside the block images in <output-directory>/<document>/. The options that describe a block are ignored: echo and its companions, label, cap, file, output-filename, pages, layout-ncol, align, and output-location.
eval: false leaves the expression as inline code, since there is no source listing to fall back on. include: false compiles and writes the image, then leaves the expression out of the sentence.
The compilation options can also be set on one expression, as attributes:
A big red `{typst} $pi r^2$`{format="png" dpi="300" foreground="#ff0000"} in a sentence.format, dpi, background, foreground, cache, classes, preamble, and input are accepted there, along with alt. Anything else is ignored; where images are written stays a global decision.
Alt text is given the same way:
The area is `{typst} $pi r^2$`{alt="pi r squared"}.Without it, the Typst source is used as the alt text.
An inline expression sets its own width, height, and margin so the result sits on the text baseline, and ignores the values configured for blocks. With output: asis in Typst output it is passed through instead, scoped in #[ ... ] with the preamble and colour bindings ahead of it, so a #let or #set inside stays local to the expression. Every other writer compiles an image, as it does for a block.
When background or foreground carries both a light and a dark value, an expression in HTML output is compiled twice and both images are emitted, marked with Quarto’s light-content and dark-content so the page shows the one matching its mode.
Brand data
Three bindings are placed ahead of every block and every inline expression, so library code can read them under known names.
| Binding | Value |
|---|---|
_typst_render_background |
The resolved background, as a Typst colour. none when unset. |
_typst_render_foreground |
The resolved foreground, as a Typst colour. none when unset. |
_typst_render_brand |
The document brand, as a dictionary in _brand.yml shape. |
Brand and theming draws the brand roles, and shows a figure compiled for both colour schemes.
_typst_render_brand always holds a color key and a typography key, each empty when the brand says nothing under it:
#let _typst_render_brand = (
"color": ("background": "#FFFAF0", "foreground": "#1A1A1A", "primary": "#E94C3D"),
"typography": ("base": ("family": "Inter"), "headings": ("family": "Inter")),
)Under color are the nine semantic roles that a chart can act on: foreground, background, primary, secondary, tertiary, success, info, warning, and danger. Quarto resolves palette aliases first, and every value is carried as written in the brand file. Only hex values reach the dictionary, because hex is what a _brand.yml reader expects. A role written in any other notation, such as red or oklch(...), is left out with a warning naming it. Under typography are the base and headings families. Sizes and weights are left out, because Typst can use a font family only when the compiler already has it.
The dictionary follows the document brand-mode, so a dark document gets the dark values. It is not itself a reason to compile twice: background: auto or foreground: auto is what produces a light and dark pair, and the dictionary then matches the side being compiled. A role or a family written for one mode only is carried into both, so a brand that gives secondary a light value and no dark value keeps that value in dark mode.
background: auto reads the same brand role as _typst_render_brand.color.background, in a different form: a Typst colour ready for fill:, rather than the _brand.yml string.
A block with no brand still compiles, because the two keys are bound empty:
#let _typst_render_brand = ("color": (:), "typography": (:))A role is a different matter: read one that the brand does not define and Typst stops. Use .at(name, default: ...) for a role the brand may leave out.
Interactions
code-window claims ```{typst} blocks at pre-quarto and rewrites them into decorated code blocks, so this filter never sees them and no figure is produced. Do not run both on the same document.
Limitations
- Inline Typst is not available in PowerPoint: Pandoc cannot put an image inside a run of text there. Blocks work normally.
- The document engine decides whether a
{typst}block reaches the filter at all. See Engines. format: htmlneeds Typst 0.15 or later, and falls back to SVG below that.