Reference

The functions and types Twyla adds to Typst’s standard library.

document

document(
  output: autostrfunction,
  title: contentnone,
  date: datetimenone,
  description: contentnone,
  kind: strauto,
  extra: any,
  draft: bool,
  body: content,
) -> content

Defines a page of the website.

Conventionally, document metadata is provided near the top of each page. with a set rule. Every field is then available on this page via #context
document.<field>
, and the whole site’s metadata is available through documents().

#set document(
  title: "Rewriting My Blog",
  date: datetime(year: 2026, month: 4, day: 12),
  description: "Why I moved off Markdown.",
  kind: "post",
)

It is also possible to create new pages inline, by constructing documents:

#context link(
    document(
        title: "Page within a page",
        output: auto,
    )[Hello from a page within a page!].url(),
)[This is a link to a page within a page]
outputautostrfunction= auto

Where to write the document, as a bundle output path — e.g. "foo/index.html" for a page called “foo”, or "foo.html". Can be used to create a page literally called “main”.

A relative path (no leading /) resolves against a base: for a full-file page, the source’s folder below content/ (content/blog/post.typblog/, so output: "extra.html"blog/extra.html); for an inline document(..), the enclosing page’s output directory. A leading / is bundle-root-absolute (output: "/feed.xml"), ignoring that base. When auto, a full-file page derives its output from the source filename and an inline document gets a fingerprinted assets/document-<hash>.html path.

A function receives the document’s full input hash, "html", and its source stem (empty for an inline document), and returns a path: (hash, ext, stem) => "previews/" + hash + "." + ext.

titlecontentnone= none
The page’s title.
datedatetimenone= none
The page’s publication date.
descriptioncontentnone= none
A short description or summary of the page.
kindstrauto= auto

What kind of page this is, e.g. "post" or "page" — used to group pages in listings and feeds, and to pick the {kind}-template a convert draft shows. If auto, defaults from the source filename:

Kind Default for
"root" content/main.typ — the site index
"dir" content/<dir>/main.typ — a section index
"page" any other file
extraany= (:)
Arbitrary extra data for your own use. Available as document.extra and as the extra field of this page’s [documents] entry. Defaults to an empty dictionary, so templates can document.extra.at(.., default: ..) without first checking for none.
draftbool= false
Whether this page is a draft. Drafts are still built, but are conventionally excluded from listings and feeds.
bodycontent
The body of an inline document. When document is called as a constructor — #document(output: "x")[stuff] — this carries stuff, which twyla hoists into its own bundle output (see [crate::compile]). #set document(..) never touches this (required positional fields aren’t settable), so the set-rule-only metadata-carrier use is unaffected.
Returns content

.url()

document.url() -> str

The page’s public URL. Two call forms:

Contextual — call it inside #context. context precedes the optional this self-positional: the #[func] macro classifies special params by name and forwards them ahead of ordinary positionals (the instance is prepended as that positional on a method call).

Returns str

documents

documents() -> array

The list of every page in the site.

Returns an array with one dictionary per page, each carrying that page’s url plus the metadata it set via #set document(..): url, title, date, description, kind, draft, and extra. Access fields with ordinary dot syntax, e.g. doc.title.

This is contextual — call it inside a #context block:

#context for doc in documents() {
  if doc.kind == "post" and not doc.draft [
    == #link(doc.url, doc.title)
    #doc.date.display()

    #doc.description
  ]
}
Returns array

asset

.url()

asset.*.url() -> str

The resolved, fingerprinted URL of this asset.

Returns str

.read()

asset.*.read(
  encoding: "utf8"none,
) -> strbytes

This asset’s bytes, to inline instead of link. Returns a UTF-8 str by default, raw bytes with encoding: none, same as read.

encoding"utf8"none= "utf8"
The encoding to read the asset with. If {none}, returns raw bytes; otherwise the bytes are decoded as UTF-8 into a string.
Returns strbytes

asset.file

asset.file(
  path: pathstr,
  output: autostrfunction,
) -> content

Pulls in a file verbatim as an asset.

#context html.elem("img", attrs: (src: asset.file("logo.svg").url()))
pathpathstr
Path to the file, relative to the calling file.
outputautostrfunction= auto
Bundle-relative output path policy.
Returns content

asset.sass

asset.sass(
  path: pathstrbytes,
  format: auto"sass""scss",
  minify: bool,
  output: autostrfunction,
) -> content

Compile a Sass/SCSS file to a fingerprinted CSS asset.

minify is a settable field, so #set asset.sass(minify: false) switches a whole page (or site) to expanded output.

#context html.elem("link", attrs: (
  rel: "stylesheet",
  href: asset.sass("main.scss").url(),
))
pathpathstrbytes
A .sass/.scss path relative to the calling file, or stylesheet source as bytes.
formatauto"sass""scss"= auto
Input syntax. For file sources, {auto} detects Sass from a .sass extension and otherwise uses SCSS. Byte sources have no extension and therefore require an explicit "sass" or "scss" format.
minifybool= true
Minify the output with grass’s compressed style (strips whitespace, comments, and other redundant characters). Part of the asset key, so the minified and expanded builds of one file resolve to distinct assets.
outputautostrfunction= auto
Bundle-relative output path policy.
Returns content

asset.svg

asset.svg(
  path: pathstrbytes,
  minify: bool,
  id: noneautostr,
  output: autostrfunction,
) -> content

Minify an SVG asset and optionally set an id on its root element.

#context {
  let icon = asset.svg("icon.svg", id: auto)
  raw-html("<use href=\"" + icon.url() + "#" + icon.elem-id() + "\"/>")
}
pathpathstrbytes
A source SVG path (relative to the calling file), or SVG data as bytes.
minifybool= true
Minify the output with svgm’s default pass set (an svgo port). Part of the asset key, so minified and verbatim builds of one file are distinct assets. A source svgm can’t parse passes through verbatim with a warning.
idnoneautostr= none
An id attribute for the root <svg> element, so <use href="…#id"> can reference the whole graphic cross-document. {none} (the default) leaves the root untouched; {auto} derives a stable id from the source content hash; a string is used verbatim. Read the resolved id back with .elem-id().
outputautostrfunction= auto
Bundle-relative output path policy.
Returns content

asset.image

asset.image(
  path: pathstrbytes,
  width: intnone,
  height: intnone,
  fit: "contain""cover""stretch",
  filter: "nearest""triangle""catmull-rom""gaussian""lanczos",
  format: auto"png""jpeg""gif""webp""avif",
  quality: int,
  output: autostrfunction,
) -> content

Pulls in an image file as an asset.

#context html.elem("img", attrs: (
  src: asset.image("photo.jpg", width: 600, format: "webp").url(),
))

By default, the image file is copied verbatim (same as file). If format is provided, the image will be transcoded. If width/height are provided, the image will be resized.

All parameters are settable, so #set asset.image(format: "webp") applies to any image asset in the scope.

pathpathstrbytes
A source image path (relative to the calling file), or image data as bytes.
widthintnone= none
Target width in pixels. With only one of width/height set the other is computed to preserve the aspect ratio; with neither, the image is not resized.
heightintnone= none
Target height in pixels. See [width](Self::width).
fit"contain""cover""stretch"= "contain"
How the image is fit into width×height when both are given: "contain" scales to fit inside the box (aspect preserved), "cover" scales and crops to fill it (aspect preserved), "stretch" forces the exact dimensions (aspect distorted). cover/stretch require both dimensions.
filter"nearest""triangle""catmull-rom""gaussian""lanczos"= "lanczos"
The resampling filter used when scaling. "lanczos" (the default) gives the best downscaling quality; cheaper options trade quality for speed.
formatauto"png""jpeg""gif""webp""avif"= auto
Output format. {auto} (the default) preserves the detected source format; otherwise the image is transcoded to the named format.
qualityint= 75
Encoder quality, 1–100, for lossy formats (JPEG, WebP, AVIF). Ignored by PNG and GIF.
outputautostrfunction= auto
Bundle-relative output path policy.
Returns content

asset.raw

asset.raw(
  data: strbytes,
  extension: str,
  output: autostrfunction,
) -> content

Emit inline data verbatim as an asset.

Use [asset.file](super::file::FileAsset) for a path that should be read and watched; asset.raw never touches the filesystem.

datastrbytes
Text or bytes to emit verbatim.
extensionstr= "bin"
Output extension, without a leading dot. This drives the generated filename and static-server content type.
outputautostrfunction= auto
Bundle-relative output path policy.
Returns content

asset.typst

asset.typst(
  source: pathstrcontent,
  format: "svg""png""pdf""html",
  ppi: int,
  minify: bool,
  output: autostrfunction,
) -> content

Compile a typst document (content or a project .typ file) to a fingerprinted asset.

format (default svg) and ppi are settable, so #set asset.typst(format: "pdf") configures a whole scope.

#context html.elem("a", attrs: (
  href: asset.typst("/resume/cv.typ", format: "pdf").url(),
))[Download my CV]
sourcepathstrcontent
The document to compile: an inline content value, or a path string to a project .typ file (resolved relative to the calling file).
format"svg""png""pdf""html"= "svg"
Output format: "svg" (the default), "png", "pdf", or "html". svg/png/pdf lay the document out as pages; html compiles it the same way the site’s pages are compiled.
ppiint= 144
Pixels per inch for the png format. Ignored by the other formats (and excluded from the cache key for them, so it never fragments their output).
minifybool= false
Minify svg output with svgm (best-effort — unparsable output warns and passes through). Ignored by the other formats (and excluded from their cache key). Off by default so raw compiles stay byte-identical to typst compile.
outputautostrfunction= auto
Bundle-relative output path policy.
Returns content

raw-html

raw-html(
  html: strbytes,
) -> content

Splice a string of raw HTML into the output verbatim.

Typst has no first-class raw-HTML node, so for now this wraps the markup in <script type="…">…</script> — a raw-text element typst won’t escape — and twyla’s post-render pass ([crate::render::resolve_raw_html_placeholders]) strips the wrapper back out. Exposing it as a builtin rather than a typst #let keeps call sites stable if we later swap the implementation (e.g. parse the HTML with html5ever and emit real typst nodes).

htmlstrbytes
The raw HTML markup to emit unescaped — a string, or bytes (e.g. from asset.file("icon.svg").read(encoding: none)) decoded as UTF-8.
Returns content

plain-text

plain-text(
  content: content,
) -> str

The plain text of some content — the same flattening typst uses to derive the document <title> from document(title: …). Useful for slugs, alt text, and other places that need a string from rich content.

contentcontent
The content to flatten to plain text.
Returns str

sys.twyla-version

sys.twyla-version: version = version(0, 1, 0)

The version of Twyla currently compiling the document, as a version value. The field can be used to detect if Twyla is being used:

#if "twyla-version" in dictionary(sys) [ Hello from Twyla ] else [ Hello from Typst ]