A Quarto filter extension that compiles ```{typst} code blocks and inline `{typst} ...` expressions to images (PNG, SVG, or PDF) using the Typst binary bundled with Quarto.
This makes Typst diagrams, figures, tables, and equations usable across all output formats (HTML, PDF via LaTeX, DOCX, RevealJS, and more).
By default, blocks are compiled to images for all output formats, including Typst.
Use output: asis for native passthrough when the output format is Typst.
quarto add mcanouil/quarto-typst-render@0.17.0This will install the extension under the _extensions subdirectory.
If you are using version control, you will want to check in this directory.
To use the extension, add the following to your document's front matter:
filters:
- typst-renderFor cross-referencing support, use timing control:
filters:
- path: typst-render
at: pre-quartoThen write Typst code blocks in your document:
```{typst}
#set text(size: 16pt)
Hello from *Typst*!
```Render Typst expressions inline using backtick code with the typst class.
The rendered image scales to match the surrounding text size.
Here is a red word `{typst} #text(red)[hello]` in the middle of a sentence.
Inline maths: `{typst} $ x^2 + y^2 = z^2 $` renders as a formula image.Global options (format, dpi, preamble, background, foreground, output) apply to inline expressions.
Block-only options (echo, eval, label, cap, alt, file, output-filename, pages, layout-ncol, output-location) are not available for inline expressions.
Provide explicit alt text for accessibility using the alt attribute:
The area is `{typst} $pi r^2$`{alt="pi r squared"} for a circle of radius r.When no alt attribute is provided, the Typst source code is used as alt text.
Note
Inline Typst is not supported for PowerPoint (PPTX) output.
Pandoc cannot embed images inside text runs in PPTX slides, so inline code is kept as-is.
Block-level {typst} code blocks work normally in PPTX.
Use comment+pipe syntax (//| key: value) at the top of the code block:
```{typst}
//| width: 10cm
//| dpi: 288
//| format: png
#align(center)[A custom-sized block.]
```Use //| label: and //| cap: for Quarto cross-references.
Generic cap and alt options work with any label prefix.
Prefix-specific variants (e.g., fig-cap, tbl-alt) override the generic options when the label matches.
Any Quarto cross-reference type is supported (fig-, tbl-, lst-, etc.), including custom types defined under crossref.custom in the document YAML.
```{typst}
//| label: fig-my-diagram
//| cap: "A captioned Typst figure."
//| alt: "Description for screen readers."
#circle(radius: 1cm, fill: blue)
```
See @fig-my-diagram for the rendered figure.Show source code alongside the rendered output, or display source only:
```{typst}
//| echo: true
$ E = m c^2 $
```
```{typst}
//| echo: fenced
$ E = m c^2 $
```
```{typst}
//| eval: false
//| echo: true
This code is shown but not compiled.
```Use echo: fenced to display source code wrapped in fenced code block markers (```{typst}), including any comment+pipe options (except echo itself).
This mirrors Quarto's native echo: fenced behaviour for computational cells.
For HTML-based output, collapse the echoed source in a <details> block with code-fold, and set the disclosure text with code-summary:
```{typst}
//| echo: true
//| code-fold: true
//| code-summary: "Show the code"
$ E = m c^2 $
```code-fold wraps only the echoed source, not the rendered output, and is ignored for non-HTML formats.
Use code-fold: show to render the block expanded by default.
The summary defaults to Code when code-summary is unset, and code-summary is rendered as Markdown.
With echo: true, Quarto code annotations work on the Typst source.
Add // <N> markers to the relevant lines, then a numbered list immediately after the block:
```{typst}
//| echo: true
#set text(size: 14pt) // <1>
$ E = m c^2 $ // <2>
```
1. Set the text size.
2. Mass-energy equivalence.Code annotations are honoured only for echo: true; with echo: false or echo: fenced the // <N> markers are stripped and annotations are ignored.
Annotations compose with code-fold.
Render an external .typ file instead of inline code:
```{typst}
//| file: path/to/file.typ
```When Typst produces multiple pages (e.g., using #pagebreak()), all pages are included by default.
Use pages to select specific pages and layout-ncol to arrange them in columns.
```{typst}
//| layout-ncol: 2
//| width: 8cm
//| height: 6cm
#align(center + horizon)[*Page 1*]
#pagebreak()
#align(center + horizon)[*Page 2*]
```Select specific pages with pages:
```{typst}
//| pages: 1
//| width: 8cm
//| height: 6cm
#align(center + horizon)[*Shown*]
#pagebreak()
#align(center + horizon)[*Hidden*]
```R, Python, or Julia cells with output: asis can output ```{typst} blocks.
The filter processes these after engine execution.
Use typst_define() to push named values from a knitr or jupyter session into every {typst} cell of the document.
Defined values are exposed as a single dict named typst_define; access them as #typst_define.<name>.
Supported value types: scalars, strings, booleans, arrays, nested objects, R data frames (column-wise), pandas/polars DataFrames (column-wise), and numpy arrays.
The helpers ship with the extension under _extensions/mcanouil/typst-render/_resources/ (once installed).
Either source()/import them, or copy-paste the body into a setup chunk.
R example (in a knitr chunk):
```{r}
#| include: false
source("_extensions/mcanouil/typst-render/_resources/typst_define.R")
```
```{r}
#| output: asis
typst_define(mtcars = head(mtcars)[, c("mpg", "cyl", "hp")], n = 42L)
```
```{typst}
n = #typst_define.n
#let df = typst_define.mtcars
#let cols = df.keys()
#table(
columns: cols.len(),
table.header(..cols.map(c => [*#c*])),
..for i in range(df.at(cols.at(0)).len()) {
cols.map(c => [#df.at(c).at(i)])
}
)
```Python example (in a jupyter chunk):
```{python}
#| include: false
import sys
sys.path.insert(0, "_extensions/mcanouil/typst-render/_resources")
from typst_define import typst_define
```
```{python}
#| output: asis
import pandas as pd
typst_define(df = pd.DataFrame({"x": [1, 2, 3], "y": [4, 5, 6]}), label = "hello")
```
```{typst}
#typst_define.label
#let df = typst_define.df
#let cols = df.keys()
#table(
columns: cols.len(),
table.header(..cols.map(c => [*#c*])),
..for i in range(df.at(cols.at(0)).len()) {
cols.map(c => [#df.at(c).at(i)])
}
)
```Caveats:
- Define-before-use ordering: a
{typst}cell that runs before anytypst_define()call has notypst_definebinding and will fail to compile if it references the name. - Identifier-named keys for dot access (
typst_define.x). Usetypst_define.at("f(x)")for keys that are not valid Typst identifiers. - Quarto
freeze: trueinteraction: changing data in a frozen chunk does not invalidate the freeze cache. Re-render with--no-cacheor remove_freeze/after edits. - Cache invalidation: any change to defined data invalidates every
{typst}cell's render cache, even cells that do not reference the changed name.
Configure the filter globally in your document YAML.
Most options can be set globally and overridden per block using comment+pipe syntax (//| key: value).
See Global-Only Options for options that cannot be overridden per block.
Inline preamble:
extensions:
typst-render:
dpi: 288
margin: "1em"
preamble: '#set text(font: "Libertinus Serif")'File-based preamble (any value ending in .typ is read as a file):
extensions:
typst-render:
preamble: "preamble.typ"Input variables (accessible via sys.inputs in Typst code):
extensions:
typst-render:
input:
theme: dark
lang: enCache cleanup removes stale files from previous renders:
extensions:
typst-render:
cache-refresh: trueBy default, compiled images are only stored in the internal cache directory (.quarto/typst-render/) with auto-generated filenames.
Set output-directory to also save copies to a predictable location, and optionally override the filename per block with output-filename.
Paths follow Quarto conventions: a leading / is relative to the project root, otherwise relative to the document directory.
Global directory (all blocks are saved automatically):
extensions:
typst-render:
output-directory: /images/typst/When output-directory is set and no output-filename is given, the filename is auto-generated from the block label (e.g., fig-diagram.png) or block counter (e.g., typst-block-1.png).
Per-block filename override:
```{typst}
//| output-filename: my-diagram.png
#circle(radius: 1cm, fill: blue)
```Combined usage:
```{typst}
//| label: fig-chart
//| output-filename: chart.svg
#rect(width: 3cm, height: 2cm, fill: eastern)
```With a global output-directory: /images/, this saves to /images/chart.svg (project root).
A per-block output-filename starting with / overrides the global directory entirely (e.g., //| output-filename: /other/result.png saves to /other/result.png).
For multi-page output, page numbers are appended before the extension (e.g., diagram1.png, diagram2.png).
For dual-mode (light/dark) rendering, -light and -dark suffixes are appended (e.g., diagram-light.svg, diagram-dark.svg).
Set output-source: true to also persist the compiled Typst source (preamble plus colour bindings plus user code) next to each saved image, using the same stem with a .typ extension.
This makes every persisted image reproducible by recompiling the saved source.
extensions:
typst-render:
output-directory: /images/typst/
output-source: trueSet text and page fill colours for rendered images.
Values can be Typst colour literals, CSS hex strings (converted automatically), auto (reads from _brand.yml), or a map with light/dark keys for theme-aware rendering.
Static colours:
extensions:
typst-render:
foreground: "eastern"
background: "luma(245)"Brand-aware colours (requires a _brand.yml with color.foreground and/or color.background defined):
extensions:
typst-render:
foreground: auto
background: autoExplicit light/dark values (HTML/Reveal.js renders both variants using Quarto's .light-content/.dark-content classes; other formats use brand-mode to select one):
brand-mode: light
extensions:
typst-render:
foreground:
light: "#1a1a2e"
dark: "#eaeaea"
background:
light: "#ffffff"
dark: "#1a1a2e"Per-block override using comment+pipe syntax:
```{typst}
//| foreground: eastern
//| background: luma(245)
#align(center)[Styled text.]
```Per-block input override using comma-separated syntax:
```{typst}
//| input: theme=light,lang=fr
#sys.inputs.at("theme")
```| Option | Type | Default | Description |
|---|---|---|---|
format |
string | (auto) | Image format: png, svg, pdf. |
dpi |
number | 144 |
Pixels per inch (PNG only). |
width |
string | "auto" |
Page width for image compilation (ignored with output: asis). |
height |
string | "auto" |
Page height for image compilation (ignored with output: asis). |
margin |
string | "0.5em" |
Page margin for image compilation; block inset with output: asis. |
background |
string|object | "none" |
Page fill colour. Accepts a Typst colour, auto (from _brand.yml), or {light, dark} map. |
foreground |
string|object | (none) | Text fill colour. Accepts a Typst colour, auto (from _brand.yml), or {light, dark} map. |
preamble |
string | "" |
Typst code or path to a .typ file prepended before user code. |
cache |
boolean | true |
Cache compiled images. Set false to skip cache (existing files are preserved). |
cache-refresh |
boolean | false |
Remove stale cache files after each render (global only). |
input |
object | (none) | Key-value pairs passed as --input flags to Typst CLI. |
file |
string | (none) | Path to external .typ file to render. |
output-directory |
string | (none) | Directory for saving compiled images. See Output Directory. |
output-filename |
string | (none) | Filename for the saved image. Leading / overrides output-directory. Auto-generated if omitted. |
output-source |
boolean | false |
Also write the compiled Typst source next to each saved image. Requires output-directory or output-filename. |
echo |
boolean|string | false |
Show Typst source code alongside output (true, false, fenced). true honours code annotations on the source. |
code-fold |
boolean|string | false |
Collapse the echoed source in a <details> block (HTML only). Use show to render expanded. |
code-summary |
string | "Code" |
Summary text for the code-fold <details> block (HTML only). |
eval |
boolean | true |
Compile Typst code to image. |
include |
boolean | true |
Include block in output. Set false to suppress entirely. |
output |
boolean|string | true |
Show rendered output. Use asis for native Typst passthrough. |
output-location |
string | (none) | Output placement in Reveal.js (fragment, slide, column, column-fragment). |
classes |
string | (none) | Space-separated CSS classes on the output image (e.g., r-stretch). |
pages |
string | "all" |
Pages to include from multi-page output: all, 1, 1-3, 2,5, 3-. |
layout-ncol |
string | (none) | Number of columns for arranging multi-page output. Omit for vertical stack. |
align |
string | (none) | Horizontal alignment: left, center, right, default. |
Any unknown option with a string value is forwarded as an HTML attribute on the output image element (e.g., //| style: "max-height: 300px;").
Values that look like booleans (true/false) must be quoted to be forwarded (e.g., //| data-lazy: "true").
These options can only be set in the document YAML and cannot be overridden per block.
| Option | Type | Default | Description |
|---|---|---|---|
root |
string | (document directory) | Root directory for Typst compilation. Relative paths resolve against the document directory; a leading / means the project root. |
font-path |
string|array | (none) | Path or list of paths to directories containing additional fonts. |
package-path |
string | (none) | Path to a directory containing Typst packages (offline/reproducible). |
| Option | Type | Description |
|---|---|---|
label |
string | Quarto cross-ref label (e.g., fig-x, tbl-y, lst-z). |
cap |
string | Caption text for the labelled block. |
alt |
string | Alternative text for accessibility. Falls back to caption, then source code. |
<prefix>-cap |
string | Prefix-specific caption (e.g., fig-cap). Overrides cap for matching labels. |
<prefix>-alt |
string | Prefix-specific alt text (e.g., fig-alt). Overrides alt for matching labels. |
| Output Format | Default Image Format |
|---|---|
| HTML / RevealJS | svg |
| LaTeX / Beamer | pdf |
| Typst | png |
| DOCX / PPTX | png |
| Other | png |
eval |
echo |
Result |
|---|---|---|
true |
false |
Image only (default). |
true |
true |
Source code block + image below. |
true |
fenced |
Fenced source code block (with markers) + image. |
false |
true |
Source code listing only. |
false |
fenced |
Fenced source code listing only (with markers). |
false |
false |
Nothing rendered (hidden block). |
The include and output options take precedence over the eval/echo matrix:
include: falsehides the entire block regardless of eval/echo settings.output: falseskips compilation and shows only the source code (if echo is enabled).output: asisuses native passthrough for Typst output; behaves astruefor other formats.
Here is the source code for a minimal example: example.qmd.
Output of example.qmd: