iTranslated by AI
Early Deep Dive into Typst 0.12.0
Introduction
This article introduces the new features and changes in Typst 0.12.0, released on 2024/10/19.
Release Notes and Deep Dive Article List
| Release Date | Version | Official Release Notes | Deep Dive Article |
|---|---|---|---|
| 2024/10/18 | 0.12.0 | Official Release Notes | Early Deep Dive into Typst 0.12.0 |
| 2025/02/19 | 0.13.0 | Official Release Notes | Early Deep Dive into Typst 0.13.0 |
| 2025/10/24 | 0.14.0 | Official Release Notes | Early Deep Dive into Typst 0.14.0 |
Pickups
Floating Figures Can Now Span Columns in Multi-column Documents
This is a featured addition highlighted by the official team. Prior to v0.11, using #figure() in a multi-column body only allowed placing floating figures within a single column. In v0.12, by specifying the scope property of figure() as "parent", it is now possible to place floating figures spanning across columns.

Example of placing a floating figure across columns. From the official blog post.
As an aside, the layout engine on the backend was reportedly rewritten to realize this feature. They also have future plans for a feature to place floating figures side-by-side with text wrapping around them. It's something to look forward to.
Official Emoji Support in PDF Output for Typst CLI
Until now, when outputting emojis using notations like #emoji.snowman in the Typst CLI, they were only displayed as outlines, as shown on the left in the figure below. Because of this, as of v0.11, workarounds such as using the svg-emojis package were necessary. In v0.12, they can now be displayed properly, as shown on the right in the figure below. This seems like it will be very useful when creating casual slides.
Note that in the author's environment, emojis were displayed correctly even when written directly as ⛄ (U+26C4) instead of #emoji.snowman.

Emoji rendering results. Left: v0.11.0, Right: v0.12.0.
Paragraphs Can Now Have Line Numbers
By specifying the numbering property of the par.line function, it is now possible to add line numbers to the paragraph margins. This is mainly intended for use cases such as academic papers.

Example of adding line numbers to paragraphs. From the official blog post.
- By default, numbers appear on the start side of the sentence (the left side for left-to-right documents). This behavior can be changed with the
number-marginproperty. - Specifying the
numbering-scopeproperty allows you to change whether the count is "incremented throughout the entire document" or "reset per page."
[Breaking Change] Paragraph Spacing Can Now Be Specified Directly via par() Function Properties
Previously, when specifying paragraph spacing, it was necessary to do something like:
#show par: set block(spacing: 2em)
However, in v0.12, the new spacing property added to par() is used like this:
#set par(spacing: 2em)
This change is treated as a breaking change, so caution is required. Those who were setting paragraph spacing with the former method should update their settings to use the latter.
[Breaking Change] Block-level Elements Are Now Affected by block() Properties
Blocks created by functions such as list(), grid(), and stack() are now collectively affected by properties specified for the block() function. This behavior differs from previous versions, so the layout of existing documents may change.
#set page(height: auto)
#set block(stroke: 1pt + red, spacing: 100pt)
#lorem(20)
#lorem(20)
- Poem
- Roses are red.
- Violets are blue.

Changes in behavior when block properties are modified via set block. Left: v0.11.0, Right: v0.12.0.
Added a Property to Suppress Page Breaks Immediately After a Block
Using the block.sticky property, you can now suppress page breaks immediately following a block. This is useful in cases where you want to avoid a page break right after a heading.
Additional Variations for import Syntax
-
In nested module structures, it is now possible to directly import grandchild elements.
// Prior to v0.11, it was necessary to write: #import "@local/my-classfile:0.1.0": component; #import component: colors // From v0.12, you can write: #import "@local/my-classfile:0.1.0": component.colors -
Imported elements can now be enclosed in parentheses, allowing import statements to span multiple lines. This is convenient when you have many functions or modules to import.
#import "@local/my-classfile:0.1.0": ( component, layout, )
Addition of the std Module
Typst defines various built-in functions, including high-frequency English words like text() and align(). Because of this, when programming in Typst, built-in functions were sometimes unintentionally overwritten (shadowed) by other variables.
// We want to define a function that defaults to center alignment, or uses the explicitly specified alignment.
// However, the code below fails to behave as intended because the built-in `align()` function is overwritten by the `align` argument.
#let myalign(align: center, body) = align(align, body)
// Therefore, it was necessary to avoid this by changing the name or other methods.
#let myalign(align_: center, body) = align(align_, body)
// It's a bit unsatisfying that the keyword argument name cannot be "align"...
#myalign(align_: right)[Example Text.]
In v0.12, the std module is available by default, resolving this issue.
// While `align` is shadowed, `std.align` remains accessible.
#let myalign(align: center, body) = std.align(align, body)
// Now you can smoothly use a keyword argument named "align"!
#myalign(align: right)[Example Text.]
Performance and File Size Improvements
While not new features, these are subtle yet welcome improvements.
-
The layout engine (the process of placing text and figures on the page) now operates with multi-threading. Placement can be performed in parallel starting from locations where explicit page breaks are made using functions like
pagebreak(). Performance improvements can be expected in large-scale documents. -
Paragraph justification, the process of aligning both ends of a paragraph, has been accelerated. It is particularly effective when there are many short paragraphs, reportedly up to six times faster.
-
By improving font subsetting methods, the file size of PDF outputs has been reduced. This is a modest but pleasing change.
Other Changes
Due to the large number of changes, we will highlight some of them here, focusing mainly on functional additions. Bug fixes and other minor updates are omitted; if you want to know all the changes, please refer to the official Changelog.
Layout Related
-
Addition of the
place.flush()function- By calling
#place.flush(), you can display all preceding floating figures before showing the subsequent content.
- By calling
-
Addition of the
skew()function- Combined with
scale()androtate(), any affine transformation can be performed.
- Combined with
-
autocan now be specified forpage.header/page.footerproperties- Specifying
autoautomatically adds page numbers according to thepage.numberingandpage.number-alignproperties. - This behavior is the same as when the property is omitted, but prior to v0.11, once a property was explicitly specified, it couldn't be reset to the default behavior. With the addition of
auto, it is now possible to return to the default.
- Specifying
-
Addition of
repeat.gapandrepeat.justifyproperties- When repeating characters like periods, you can now explicitly specify the spacing between characters. You can also specify whether to extend the repetition to fit the width.
-
frunits can now be used for theheightproperty ofblock,image,rect,square,ellipse, andcircle- Fractions (
fr) are units that indicate the ratio when dividing the length of the parent element, similar to how they are used for grid widths. If you place two blocks withheight: 1frside-by-side, they will fill the gap so that both blocks have the same height.
Example
#set page(width: 200pt, height: 200pt, margin: 10pt) #block(width: 100%, height: 1fr, fill: red.lighten(80%), radius: 5pt)[ #align(center + horizon)[#lorem(5)] ] #block(width: 100%, height: 1fr, fill: blue.lighten(80%), radius: 5pt)[ #align(center + horizon)[#lorem(5)] ]
- Fractions (
-
Lengths (not relative values) can now be directly specified for the
xandyproperties of thescale()function- In addition to specifications like "expand the width by 3 times," you can now specify "expand so that the width becomes
300pt."
- In addition to specifications like "expand the width by 3 times," you can now specify "expand so that the width becomes
-
The values of
block.aboveandblock.belowcan now be retrieved within context expressions.
Typography Related
-
The default font is now "Libertinus Serif"
- This is the successor font to "Linux Libertine," which was the original default.
- This change may cause slight variations in typesetting results.
-
Warnings are now issued when an unavailable font family is specified
- Previously, such specifications were simply ignored.
-
Improved the algorithm for the smart quotes feature
-
Added the
text.costsproperty to allow customization of cost function weights in the line-breaking algorithm- You can specify weight ratios for the following costs:
-
hyphenation(cost of breaking a single word with hyphenation) -
runt(cost of a paragraph ending with a single word) -
widow(cost of placing only the final line of a paragraph on the next page) -
orphan(cost of placing only the first line of a paragraph on the previous page)
-
"Typst math mode" can now be specified as a highlighting language for code blocks
- By specifying
typmas the language, you can write and highlight only the math portions of Typst.
- By specifying
-
Basic internationalization (basic i18n) for the following languages:
- Galician
- Catalan
- Latin
- Icelandic
- Hebrew
-
Implemented "hyphenation duplication" for some languages
- Specifically for the following languages:
- Czech
- Croatian
- Lower Sorbian
- Polish
- Portuguese
- Slovak
- Spanish
- In these languages, when hyphenating, it is reportedly necessary to place a hyphen at the beginning of the next line as well as the end of the current line. https://github.com/typst/typst/issues/3235
- Specifically for the following languages:
-
smallcaps()is now an element function that can be specified withshoworsetrules- This makes it possible to do things like changing the font only when
smallcaps()is used.
- This makes it possible to do things like changing the font only when
-
Syntax highlighting can now be turned off by specifying
nonefor theraw.themeproperty -
Multiple numbers can now be specified for the
text.stylistic-setproperty
Mathematics
-
Block-level equations can now span across multiple pages
- This behavior can be disabled with
show math.equation: set block(breakable: false).
- This behavior can be disabled with
-
Matrix and vector sizes are now more consistent
-
I haven't grasped all the details, but it seems they are less affected by height differences caused by scripts and other elements.

Appearance when composing a matrix. Note the difference in bracket sizes. Left: v0.11.0, Right: v0.12.0.
-
-
The
stretchfunction was added, allowing glyphs like equal signs and arrows to be stretched.- By writing
$ H stretch(=)^"define" U + p V $, you can create an equal sign stretched to the width of "define". - Only certain glyphs can be stretched, and this depends on the math font.
- By writing
-
The
mat.delim,vec.delim, andcases.delimproperties now accept any Unicode character that can be considered a delimiter or fence.- A fence is a character such as
"|"(U+007c), for example. - However, specifications like
delim: "||"are no longer possible. It needs to be replaced with something likedelim: bar.double.
- A fence is a character such as
-
The
vec.alignandmat.alignproperties have been added, allowing you to specify the alignment of matrix and vector elements.- Vectors composed of integers, for example, might look better when right-aligned.
-
Addition of
underparen,overparen,undershell, andovershellfunctions- These are part of the
underbracefamily. - "Shell brackets" are what are known as tortoise shell brackets
〔〕.
- These are part of the
-
~is now interpreted as a binary relation-
~can now be used as a shorthand for what was previously written astilde.op. - Note that parts previously written simply as tildes will now also be interpreted as
tilde.op.
-
Other Changes in Notation and Typesetting
-
Restrictions on the
#set document()rule have been relaxed, allowing it to be written outside the beginning of the document. -
The
spacingproperty inlist(),enum(), andterm()is now reflected even in tight lists. -
In tight lists, the way spacing before the list is added has changed; it now becomes tight only when preceded by a paragraph.
-
The
quote()element is now locatable (searchable viatypst query, etc.). -
The
hanging-indentproperty was added to theheading()function to adjust the appearance of headings spanning multiple lines.- Default headings have also been improved; when spanning multiple lines, they are now automatically indented by the width of the numbering, such as "1."
-
The
fill-ruleproperty was added to thepath()andpolygon()functions.- The behavior when filling polygons with intersecting edges, like a star, can be adjusted using two values:
"non-zero"or"even-odd".
- The behavior when filling polygons with intersecting edges, like a star, can be adjusted using two values:
-
Addition of the
decimaltype- Previously, the only type for decimals was the
floattype (floating-point numbers), but a newdecimaltype (fixed-point numbers) has been added. This allows for precise decimal calculations.
- Previously, the only type for decimals was the
-
Addition of methods to the
arraytype-
to-dict(): Converts an array of the form((k1, v1), (k2, v2), ...)into a dictionary(k1: v1, k2: v2, ...). -
reduce(): Similar to thefoldmethod, but uses the first element of the array as the initial value. -
windows(): Creates sliding windows((1, 2, 3), (2, 3, 4), (3, 4, 5))from(1, 2, 3, 4, 5,).
-
-
Added the
exactargument to thearray.zip()method- When
trueis specified for theexactargument, an error occurs if the number of elements in the two combined arrays do not match. Whenfalseis specified, the remainder of the longer array is ignored as before.
- When
Exporting
-
Partial support for PDF/A
- By enabling
PDF/Aduring export, it is now possible to output PDFs in a format more suitable for long-term preservation.- In the CLI, this can be enabled by specifying the
--pdf-standard a-2boption.
- In the CLI, this can be enabled by specifying the
- Currently, only PDF/A-2b is supported.
- By enabling
-
Setting the
page.fillproperty tononenow results in a transparent background when exporting to PDF or SVG- To specify the previous behavior (transparent background for PDF, white background for PNG and SVG), set it to
auto.
- To specify the previous behavior (transparent background for PDF, white background for PNG and SVG), set it to
CLI and Tools
-
Added the
--pagesoption- It is now possible to specify specific page numbers when exporting.
-
Added
--package-pathand--package-cache-pathoptions -
Added the
--ignore-system-fontsflag- Disables the loading of system fonts. This is effective for reducing system-dependent elements and creating documents with higher reproducibility.
-
When exporting to multiple image files,
t(total pages),p(current page), and0p(zero-padded page) can now be specified in the output filename
Conclusion
As this is the first version update in about five months, it feels like there were a very large number of changes. I have been preparing this article since the initial release of 0.12.0-rc1, but there were so many eye-catching feature additions and bug fixes that it was quite a task to summarize them all. It was mentioned that this release was delayed due to a large-scale re-implementation, so the next version might arrive a bit sooner.
If you find any errors in this article, please let me know in the comments.
Discussion