Skip to content

mcanouil/quarto-typst-render

Repository files navigation

Typst Render Extension For Quarto

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.

Installation

quarto add mcanouil/quarto-typst-render@0.17.0

This will install the extension under the _extensions subdirectory.

If you are using version control, you will want to check in this directory.

Usage

To use the extension, add the following to your document's front matter:

filters:
  - typst-render

For cross-referencing support, use timing control:

filters:
  - path: typst-render
    at: pre-quarto

Then write Typst code blocks in your document:

```{typst}
#set text(size: 16pt)
Hello from *Typst*!
```

Inline Expressions

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.

Per-Block Options

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.]
```

Cross-Referencing

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.

Echo and Eval Control

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.

External File Rendering

Render an external .typ file instead of inline code:

```{typst}
//| file: path/to/file.typ
```

Multi-Page Output

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*]
```

Engine-Generated Blocks

R, Python, or Julia cells with output: asis can output ```{typst} blocks. The filter processes these after engine execution.

Passing Data from R/Python

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 any typst_define() call has no typst_define binding and will fail to compile if it references the name.
  • Identifier-named keys for dot access (typst_define.x). Use typst_define.at("f(x)") for keys that are not valid Typst identifiers.
  • Quarto freeze: true interaction: changing data in a frozen chunk does not invalidate the freeze cache. Re-render with --no-cache or 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.

Configuration

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: en

Cache cleanup removes stale files from previous renders:

extensions:
  typst-render:
    cache-refresh: true

Output Directory

By 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: true

Foreground and Background Colours

Set 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: auto

Explicit 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")
```

Options

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").

Global-Only Options

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

Per-Block Cross-Referencing Options

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.

Auto-Selected Image Format

Output Format Default Image Format
HTML / RevealJS svg
LaTeX / Beamer pdf
Typst png
DOCX / PPTX png
Other png

Echo/Eval Behaviour

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: false hides the entire block regardless of eval/echo settings.
  • output: false skips compilation and shows only the source code (if echo is enabled).
  • output: asis uses native passthrough for Typst output; behaves as true for other formats.

Example

Here is the source code for a minimal example: example.qmd.

Output of example.qmd:

About

Quarto extension to compile Typst code blocks to images (PNG, SVG, PDF) for use across all output formats.

Topics

Resources

License

Stars

Watchers

Forks

Sponsor this project

 

Contributors