Typst is a new markup-based typesetting system that is designed to be as powerful as LaTeX while being much easier to learn and use. Typst has:
- Built-in markup for the most common formatting tasks
- Flexible functions for everything else
- A tightly integrated scripting system
- Math typesetting, bibliography management, and more
- Fast compile times thanks to incremental compilation
- Friendly error messages in case something goes wrong
This is a production-focused fork of Typst. The service that motivated it runs Typst in serverless workers with a strict memory limit, while compiling documents that contain large raster and SVG assets. Upstream's normal image path is excellent for ordinary documents, but a large full-bleed asset can make several full-size copies live at once: the decoded source, a render texture, the page canvas, and the encoded output. That is enough to make an otherwise valid document exceed a small worker's RAM limit.
The fork therefore concentrates on reducing peak memory rather than changing
Typst's language or document output. Small assets keep the established
paths, where they are already fast; large assets get bounded-memory paths
instead — see the scenario table below for exactly which path applies where
and why. The full change set is in the commit history (git log upstream/main..HEAD); the intended end state is to upstream it in focused,
reviewable pieces and merge the fork back into the main Typst repository.
The additional CLI controls are:
# Bound PNG export memory (MiB); lower values trade speed for RAM.
typst compile --max-memory 512 --ppi 72 poster.typ poster.png
# Choose a faster PNG compression effort for large exports.
typst compile --png-compression fastest poster.typ poster.png--max-memory is a PNG-export budget for the render/encode portion of a
compile, not a hard cgroup guarantee: document layout, fonts, and process
overhead still need room. For serverless deployment, leave headroom between
the flag and the worker's actual limit.
The numbers below were measured with bench/ (default
7200×18000-pixel poster) comparing this checkout against upstream a51e0280
built from a clean worktree, both release builds, on the same host back to
back. Peak RSS is process resident memory; results vary with OS, allocator,
fonts, and filesystem cache — regenerate with the command further down
rather than trusting these across machines.
Time
| Scenario | Stock | Fork |
|---|---|---|
| Opaque PNG export | 2.43 s | 849 ms |
| Alpha PNG export | 2.57 s | 711 ms |
| SVG export | 2.02 s | 324 ms |
Peak RSS
| Scenario | Stock | Fork |
|---|---|---|
| Opaque PNG export | 1874 MiB | 51 MiB |
| Alpha PNG export | 1998 MiB | 35 MiB |
| SVG export | 1510 MiB | 42 MiB |
Output size
| Scenario | Stock | Fork |
|---|---|---|
| Opaque PNG export | 536 KiB | 2.4 MiB |
| Alpha PNG export | 536 KiB | 2.4 MiB |
| SVG export | 613 KiB | 2.5 MiB |
Output size is the odd one out: the fork's PNG is larger, not smaller — see below.
Two fork-only variants isolate specific effects, both on the opaque fixture:
| Variant | Time | Peak RSS | Output size |
|---|---|---|---|
--max-memory 512 |
872 ms | 168 MiB | 2.4 MiB |
--png-compression high (matches stock's ~536 KiB output size) |
1.45 s | 51 MiB | 527 KiB |
The last row matters: the fork's default --png-compression is fast,
which is itself faster than stock's (fixed, harder) compression — some of
the plain time gap above is that default, not only the rendering-path
rewrite. At matched output size, the fork is still ~1.7× faster and uses
~37× less memory, so the memory result holds independent of the compression
default.
The fork's behavior is deliberately scenario-dependent:
| Scenario | Stock path | Fork path | Expected benefit |
|---|---|---|---|
| Opaque, native-resolution raster | Full texture plus compositing | Direct blit into the destination | Avoids a full-size texture |
| Alpha raster at native resolution | General image compositing | Alpha-aware direct path when safe; fallback otherwise | Avoids unnecessary conversion copies |
| Transformed raster | General resampling path | Specialized blitting/resampling paths | Smaller transient buffers |
| Large SVG | Rasterize a full placed texture | Render directly into the destination canvas | Memory follows the destination/band |
| Small SVG | Texture path | Existing texture path | Keeps the fast ordinary case |
| Large PNG export | Full page canvas and encoded output | Streamed horizontal bands and output | Peak memory is independent of page height |
| Large source file | Heap-backed file bytes | Memory-mapped file bytes | Lets the OS reclaim clean pages under pressure |
Run the reproducible comparison in bench/. It generates
deterministic fixtures, measures wall time and peak RSS for the same
scenarios with an upstream binary and this fork, and prints a human-readable
comparison table. The harness intentionally takes binaries as arguments so a
comparison can be made against any pinned upstream commit, not an
accidentally different local build:
cargo build --release
cargo run --release -p typst-bench -- \
/path/to/upstream/target/release/typst \
target/release/typstThe upstream binary is run without fork-only flags; the constrained fork case
uses --max-memory 512. See the harness README for fixture sizes, system
requirements, and how to pin upstream/main before building the baseline.
The implementation-level budget checks are ordinary Rust tests and do not require the large fixture:
cargo test -p typst-cli --bin typst band_budgetThis repository contains the Typst compiler and its CLI, which is everything you need to compile Typst documents locally. For the best writing experience, consider signing up to our collaborative online editor for free.
A gentle introduction to Typst is available in our documentation. However, if you want to see the power of Typst encapsulated in one image, here it is:
Let's dissect what's going on:
-
We use set rules to configure element properties like the size of pages or the numbering of headings. By setting the page height to
auto, it scales to fit the content. Set rules accommodate the most common configurations. If you need full control, you can also use show rules to completely redefine the appearance of an element. -
We insert a heading with the
= Headingsyntax. One equals sign creates a top level heading, two create a subheading and so on. Typst has more lightweight markup like this; see the syntax reference for a full list. -
Mathematical equations are enclosed in dollar signs. By adding extra spaces around the contents of an equation, we can put it into a separate block. Multi-letter identifiers are interpreted as Typst definitions and functions unless put into quotes. This way, we don't need backslashes for things like
floorandsqrt. Andphi.altapplies thealtmodifier to thephito select a particular symbol variant. -
Now, we get to some scripting. To input code into a Typst document, we can write a hash followed by an expression. We define two variables and a recursive function to compute the n-th fibonacci number. Then, we display the results in a center-aligned table. The table function takes its cells row-by-row. Therefore, we first pass the formulas
$F_1$to$F_8$and then the computed fibonacci numbers. We apply the spreading operator (..) to both because they are arrays and we want to pass the arrays' items as individual arguments.
Text version of the code example.
#set page(width: 10cm, height: auto)
#set heading(numbering: "1.")
= Fibonacci sequence
The Fibonacci sequence is defined through the
recurrence relation $F_n = F_(n-1) + F_(n-2)$.
It can also be expressed in _closed form:_
$ F_n = round(1 / sqrt(5) phi.alt^n), quad
phi.alt = (1 + sqrt(5)) / 2 $
#let count = 8
#let nums = range(1, count + 1)
#let fib(n) = (
if n <= 2 { 1 }
else { fib(n - 1) + fib(n - 2) }
)
The first #count numbers of the sequence are:
#align(center, table(
columns: count,
..nums.map(n => $F_#n$),
..nums.map(n => str(fib(n))),
))Typst's CLI is available from different sources:
-
You can get sources and pre-built binaries for the latest release of Typst from the releases page. Download the archive for your platform and place it in a directory that is in your
PATH. To stay up to date with future releases, you can simply runtypst update. -
You can install Typst through different package managers. Note that the versions in the package managers might lag behind the latest release.
- Linux:
- View Typst on Repology
- View Typst's Snap
- macOS:
brew install typst - Windows:
winget install --id Typst.Typst
- Linux:
-
If you have a Rust toolchain installed, you can install
- the latest released Typst version with
cargo install --locked typst-cli - a development version with
cargo install --git https://github.com/typst/typst --locked typst-cli
- the latest released Typst version with
-
Nix users can
- use the
typstpackage withnix-shell -p typst - build and run the Typst flake with
nix run github:typst/typst-flake -- --version.
- use the
-
Docker users can run a prebuilt image with
docker run ghcr.io/typst/typst:latest --help.
Once you have installed Typst, you can use it like this:
# Creates `file.pdf` in working directory.
typst compile file.typ
# Creates a PDF file at the desired path.
typst compile path/to/source.typ path/to/output.pdfYou can also watch source files and automatically recompile on changes. This is faster than compiling from scratch each time because Typst has incremental compilation.
# Watches source files and recompiles on changes.
typst watch file.typTypst further allows you to add custom font paths for your project and list all of the fonts it discovered:
# Adds additional directories to search for fonts.
typst compile --font-path path/to/fonts file.typ
# Lists all of the discovered fonts in the system and the given directory.
typst fonts --font-path path/to/fonts
# Or via environment variable (Linux syntax).
TYPST_FONT_PATHS=path/to/fonts typst fontsFor other CLI subcommands and options, see below:
# Prints available subcommands and options.
typst help
# Prints detailed usage of a subcommand.
typst help watchIf you prefer an integrated IDE-like experience with autocompletion and instant preview, you can also check out our free web app. Alternatively, there is a community-created language server called Tinymist which is integrated into various editor extensions.
The main places where the community gathers are our Forum and our Discord server. The Forum is a great place to ask questions, help others, and share cool things you created with Typst. The Discord server is more suitable for quicker questions, discussions about contributing, or just to chat. We'd be happy to see you there!
Typst Universe is where the community shares templates and packages. If you want to share your own creations, you can submit them to our package repository.
If you had a bad experience in our community, please reach out to us.
We love to see contributions from the community. If you experience bugs, feel free to open an issue. If you would like to implement a new feature or bug fix, please follow the steps outlined in the contribution guide.
To build Typst yourself, first ensure that you have the latest stable Rust installed. Then, clone this repository and build the CLI with the following commands:
git clone https://github.com/typst/typst
cd typst
cargo build --releaseThe optimized binary will be stored in target/release/.
Another good way to contribute is by sharing packages with the community.
IPA: /taɪpst/. "Ty" like in Typesetting and "pst" like in Hipster. When writing about Typst, capitalize its name as a proper noun, with a capital "T".
All of Typst has been designed with three key goals in mind: Power, simplicity, and performance. We think it's time for a system that matches the power of LaTeX, is easy to learn and use, all while being fast enough to realize instant preview. To achieve these goals, we follow three core design principles:
-
Simplicity through Consistency: If you know how to do one thing in Typst, you should be able to transfer that knowledge to other things. If there are multiple ways to do the same thing, one of them should be at a different level of abstraction than the other. E.g. it's okay that
= Introductionand#heading[Introduction]do the same thing because the former is just syntax sugar for the latter. -
Power through Composability: There are two ways to make something flexible: Have a knob for everything or have a few knobs that you can combine in many ways. Typst is designed with the second way in mind. We provide systems that you can compose in ways we've never even thought of. TeX is also in the second category, but it's a bit low-level and therefore people use LaTeX instead. But there, we don't really have that much composability. Instead, there's a package for everything (
\usepackage{knob}). -
Performance through Incrementality: All Typst language features must accommodate for incremental compilation. Luckily we have
comemo, a system for incremental compilation which does most of the hard work in the background.
We'd like to thank everyone who is supporting Typst's development, be it via GitHub sponsors or elsewhere. In particular, special thanks1 go to:
- Posit for financing a full-time compiler engineer
- NLnet for supporting work on Typst via multiple grants
through the NGI Zero Core fund:
- Work on HTML export
- Work on PDF accessibility
- Science & Startups for having financed Typst development from January through June 2023 via the Berlin Startup Scholarship
- Zerodha for their generous one-time sponsorship
Footnotes
-
This list only includes contributions for our open-source work that exceed or are expected to exceed €10K. ↩