Cached at:
06/29/26, 04:31 PM
TL;DR: Typst achieves efficient incremental compilation and real-time preview through constraint memoization (comemo) and a pure function design, making the language and compiler work together.
## Introduction: Typst's Design Goals
Typst is a markup-based typesetting system with its own markup language, styling system (`set` and `show` rules), a full scripting layer, and an introspection system. It is designed to be easy to learn and pleasant to use, with the core goal of achieving **real-time preview**—entirely feasible given 2026's hardware. The key is to **co-design the language and its implementation** to support high-performance incremental compilation.
## The Core of Incremental Compilation: Memoization
To reuse already compiled results when editing a document, Typst uses **memoization** to remember intermediate compilation results.
### Simple Memoization of Layout
A layout function takes inputs like a paragraph and a width and returns a fixed-sized frame. It is a pure function with no side effects. So simply hash the inputs (using `xxhash`) and store the result in a hash map. This is the most basic caching strategy, and it is remarkably effective in reducing layout overhead.
### Module Evaluation and Constraint Memoization
The scripting system includes modules, organized using `include` and `import`. If we memoize the `eval` function directly, its inputs include a source file and the filesystem. But the filesystem changes when external files change, causing all `eval` calls to be invalidated.
Typst uses the **comemo** (constraint memoization) crate: the filesystem is wrapped in a `Tracked` type. Each time `eval` calls `fs.load`, it records the actual file path that was read and its hash, forming a set of “constraints”. After evaluation finishes, the constraints and output are stored together in the cache. On the next call, we hash the non-tracked arguments to find the entry, then check each constraint individually. The cache is invalidated only when an actually-read file changes, avoiding a full recompilation.
## Functions and Purity
User functions in Typst must also be cacheable. The idea is to enforce **pure functions**—no mutable references, no global state. Does this turn the script language into Haskell? Not really. Typst is more like “deterministic Rust without references”, allowing local mutability (e.g., `for` loops), but mutability is encapsulated inside functions. Efficient data passing is achieved through value semantics and reference counting (`Arc`), without needing lifetimes. For example, when returning a vector, cloning into the cache only requires `arc_bump` (incrementing the reference count), offering great performance. Local mutability is implemented via `Arc::make_mut`: check if the reference is unique; if so, mutate in place; otherwise, clone then mutate.
This design ensures cache correctness while keeping the script language easy to use.
## Challenges of a Typesetting System: Counters and Table of Contents
Pure functions cannot directly use mutable counters (like section numbers) or query the internal state of the document (like a table of contents). Counters seem simple, but a table of contents and cross-references are actually two sides of the same coin: both require document introspection.
### LaTeX's Multiple Iterations
LaTeX solves this by writing information to auxiliary files (e.g., `.toc`) and running the compiler multiple times. Users need to run repeatedly, using `latexmk` to automatically iterate until stable. This is a “keep retrying until convergence” approach.
### Typst's Introspection System
Instead of relying on mutability, Typst provides **introspection** capabilities: the document can observe its own structure. For example, one can ask “where is the first heading in the document?” and obtain its page coordinates. In this way, symbolic numbering (like chapter numbers) and the table of contents can be implemented via introspection rather than mutable counters. This still respects the pure function constraint while solving traditional typesetting pain points in a more declarative manner.
## Summary
Typst achieves efficient incremental compilation by tightly coupling the compiler's memoization features with a pure function language design. Layout caching, constraint-memoized module evaluation, enforced function purity, and the introspection system together form the foundation for Typst's real-time preview. This co-design approach allows the language syntax and implementation to reinforce each other, rather than becoming a burden.
Source: https://youtu.be/yWWVhbyOWWE