Reference

Every option the typst-render extension accepts.

The complete typst-render configuration: the global options, the per-block options, output control, and the interactions to know about.

Enabling the filter

filters:
  - typst-render
Important

Reference 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.

How each engine treats a {typst} block.
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.

Options 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.

Options accepted on 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.

Bindings available inside a {typst} block.
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

Caution

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: html needs Typst 0.15 or later, and falls back to SVG below that.
Back to top