Typst Conditionals & Loops
#if, #else, #for, #while. Control flow that lets you compute, iterate, and conditionally include content.
Conditionals
#let score = 0.85
// Inline
The model is #if score > 0.7 [accurate] else [inaccurate].
// Block form
#if score > 0.9 [
Excellent performance.
] else if score > 0.7 [
Good performance.
] else if score > 0.5 [
Acceptable performance.
] else [
Poor performance.
]
// Code-block style (for computation)
#let category = if score > 0.7 { "good" } else { "bad" }For loops
// Iterate over an array
#let methods = ("Random", "Linear", "Neural")
- #for method in methods [#method, ]
// With index
#for (i, method) in methods.enumerate() [
#(i + 1). #method \
]
// Range
#for i in range(1, 6) [
Item #i \
]
// Iterate over dictionary
#let scores = (
random: 0.5,
linear: 0.7,
neural: 0.92,
)
#for (name, score) in scores.pairs() [
#name: #score \
]While loops
#let n = 1
#while n < 1000 {
// Compute something
n = n * 2
}Less common than #for in document construction. Useful for accumulator-driven logic or numeric algorithms.
Switchable document modes
#let mode = "thesis" // "thesis" or "paper"
= Introduction
This is the introduction.
#if mode == "thesis" [
// Full thesis: include extended methodology
== Extended Methodology
This section only appears in the thesis version,
with full derivations and additional context.
]
= Results
Main results here.
#if mode == "thesis" [
// Include appendices only in thesis mode
#include "appendices/proofs.typ"
#include "appendices/data.typ"
]Toggle mode to compile two outputs (paper vs thesis) from one source.
Generate a table from data
#let results = (
("Random", 0.50, 12.3),
("Linear", 0.71, 5.8),
("Neural", 0.92, 1.2),
)
#table(
columns: 3,
table.header([Method], [Accuracy], [Time (s)]),
..for row in results {
(row.at(0), str(row.at(1)), str(row.at(2)))
}
)The .. spread operator flattens the loop's output into table arguments. Each row contributes 3 cells.
Common mistakes
- Mixing
{}and[]incorrectly.{}is code,[]is content.#if x { [content] }works; so does#if x [content]. - No
#on inline if. Mid-sentence conditionals need#: "The result is #if x [yes] else [no]." - Comparing strings with
==. Works fine in Typst:if mode == "thesis". - Forgetting
else ifsyntax. Useelse if condition [content], notelseif(no space).
TypeTeX is a free in-browser Typst editor — write a loop, see results in milliseconds.
Try TypeTeX freeFrequently Asked Questions
#if condition [content if true] else [content if false]. Or with code blocks: #if x > 0 { [positive] } else { [negative] }. The brackets [] denote content, the braces {} denote code. For chains: #if x > 0 [positive] else if x < 0 [negative] else [zero].
#for item in array [#item, ]. The body in brackets is content, evaluated once per item. Iterate with index: #for (i, item) in array.enumerate() [#i: #item]. The .enumerate() method gives index-value tuples.
#for i in range(0, 10) [#i ]. range(start, end) produces 0..end-1. range(start, end, step) for custom step. Or for ten of the same: #for _ in range(10) [content]. Useful for repeating layouts or generating numbered items programmatically.
Yes: 'The result is #if score > 0.5 [positive] else [negative].' Inline conditional content. Whitespace around the #if matters — use as needed for the surrounding sentence flow.
#for (key, value) in dict.pairs() [#key: #value]. .pairs() returns an array of (key, value) tuples. .keys() returns just keys, .values() just values. Useful for generating tables from structured data.
Define a mode variable: #let mode = "thesis". Then conditionally include content: #if mode == "thesis" [#include "appendices/full-proofs.typ"]. Lets you compile two outputs (one short paper, one full thesis) from the same source by toggling one variable.
{} is a code block — multiple statements, returns the last expression. [] is a content block — markdown-style content. Inside #if: [content] is shorthand for { [content] }. Use {} when you need multiple operations; use [] when you just want content.
Use the break keyword: #for x in items { if condition { break }; ... }. continue skips to the next iteration. Most Typst code is functional-style (mapping, filtering with .filter() and .map()), but break/continue work for imperative loops.
Yes: #table(columns: 2, ..for row in data { (row.name, row.value) }). The .. is the spread operator — flattens the loop's array into table arguments. Handy for tables with dozens of rows from a CSV-like source.