Hacking Typst: Better Vertical Balance in Two-Column Pages

Typst
typesetting
free software
Author

Johannes Titz

Published

August 9, 2026

While refining the Typst source for my Essential R Cheatsheets, I kept noticing a small but persistent typesetting problem. The development version of Typst can already balance content across columns, yet the two columns do not always finish at quite the same visual height. On a dense page, even a few points of unused space at the bottom can be surprisingly noticeable.

The gap that bothered me

Page 50 of the working book is a good example. In the original layout, the right column extends visibly farther down, leaving an awkward strip of white space below the left column.

The red arrow marks the unused space below the left column that prompted the experiment.

My immediate reaction was: this should be easy to prevent, so why does Typst not do it automatically?

The page contains many ordinary vertical gaps: between paragraphs, around headings and blocks, and between individual lines. Instead of collecting the entire mismatch in one conspicuous space at the bottom, Typst could distribute it across all those gaps. Each adjustment would be tiny—ideally too small to notice on its own.

I did not know whether this was typographically unorthodox, whether it would create awkward edge cases, or whether the extra layout work would compromise Typst’s blazing speed. There was only one sensible way to find out: try it.

Sharing the work across many gaps

The prototype adds vertical justification to column layout:

#set page(columns: 2)
#set columns(
  balanced: true,
  justify: true,
  spacing-stretch: 0.6pt,
  spacing-shrink: 0.6pt,
  leading-stretch: 0.12pt,
  leading-shrink: 0.12pt,
)

Ordinary paragraph and block gaps may grow or shrink by at most 0.6 points. The space between lines gets the much smaller limit of 0.12 points. A single change is practically invisible, but a page contains many gaps and many lines, so together they provide enough flexibility to improve the bottom alignment. Explicit spacing and the font size remain untouched.

These limits are user-configurable. A conservative setting can prioritize an almost imperceptible result; a more generous setting gives Typst greater freedom on difficult pages.

I initially tried limits of 0.5 points and 0.1 points. That already came close, but left a tiny residual mismatch on this page. Raising them to 0.6 points and 0.12 points makes the two bottom edges coincide at the rendered resolution while keeping every individual adjustment very small.

Here is the same page after the adjustment:

After vertical justification, the columns finish at the same height.

Choosing a sensible target

The algorithm does not blindly stretch every column to the bottom of the page. Each column reports the range of heights it can reach within the configured limits. Typst then chooses a common reachable target. If perfect alignment is impossible, it chooses the target that leaves the smallest remaining difference. Final partial pages and explicit breaks keep their natural height.

That distinction matters. Filling every column to the bottom would make short chapter endings look strangely stretched. The goal is not to fill space at any cost, but to improve alignment wherever the available, visually safe adjustments make that possible.

Speed

In a quick warm benchmark of the complete book, I could not measure a meaningful slowdown compared with regular Typst. That was important to me: Typst’s speed is the main reason I use it.

Scope

The mechanism is not inherently limited to two-column documents. It can also justify full pages in a single-column layout. Still, the benefit is most obvious when two neighboring columns should end together.

Installing the experiment on Arch Linux

I added a PKGBUILD that compiles the flex-spacing-prototype branch and installs it as a drop-in replacement for Typst. It deliberately conflicts with the official typst package so that Pacman cannot leave two different /usr/bin/typst binaries on the system.

Download the file into an empty directory, inspect it, and build it with makepkg:

mkdir typst-flex-spacing-build
cd typst-flex-spacing-build
curl -O https://johannestitz.com/post/2026-08-09-typst-vertical-justification/PKGBUILD
less PKGBUILD
makepkg -si

The build follows the experimental Git branch, so rebuilding the package later will pick up newer commits. To return to the official Arch package, run:

sudo pacman -S typst

A prototype, for now

This is deliberately still an experiment. The implementation and its tests are available on the flex-spacing-prototype branch of my Typst fork. I want to try it on more real documents before settling on an API and deciding whether it is ready for a pull request—if it should become one at all.

The book behind the example

If the example made you curious about the content, Essential R Cheatsheets collects the R and statistics references I actually want within reach while working. The current edition is available in several print formats on the Amazon series page.