Universe

Reactive Documents for Typst

Loom transforms Typst from a linear typesetting system into a reactive engine. It enables bidirectional data flow, allowing your document to “think” before it renders.

📖 Read the Documentation


Why Loom?

Typst’s linear execution makes complex architectural patterns difficult. Loom solves two primary headaches:

  • Aggregation: Children emit Signals that bubble up, allowing parents to calculate data (like total prices or calories) before rendering the final output.
  • Global Dependencies: Through a Weave Loop (multiple passes), a component at the end of the document can update a summary or counter on page one.

Installation

Import Loom v0.1.1 from the package preview:

#import "@preview/loom:0.1.1"

// 1. Construct a unique instance for your project.
// The key (<my-project>) isolates your components from other libraries.
#let (weave, motif, prebuild-motif) = loom.construct-loom(<my-project>)

// 2. Export the specific tools you want to use.
// This keeps your API clean for the rest of your document.

// The Engine
#let weave = weave

// The Component Constructors
#let managed-motif = motif.managed
#let compute-motif = motif.compute
#let content-motif = motif.content
#let data-motif = motif.data
#let motif = motif.plain

// Prebuild Motifs
#let apply = prebuild-motif.apply
#let debug = prebuild-motif.debug
#let static = prebuild-motif.static

Quick Start

#import "@preview/loom:0.1.1"

// 1. Initialize Loom
#let (weave, motif) = loom.construct-loom(<my-project>)

// 2. Define a data-emitting component
#let ingredient(price) = (motif.data)("ing", measure: _ => (price: price))

// 3. Define a parent that sums child data
#let recipe(name, body) = (motif.plain)(
  measure: (ctx, children) => {
    let total = loom.query.sum-signals(children, "price")
    (none, (price: total))
  },
  draw: (ctx, public, view, body) => block[
    *#name* (Total: \$#view.price) \ #body
  ],
  body
)

#show: weave.with()

#recipe("Tomato Soup")[
  #ingredient(2.50)
  #ingredient(1.50)
  Ingredients listed here...
]

Real-World Usage

For a complex implementation, see invoice-pro, an invoicing template that utilizes Loom for heavy data aggregation.


Core Concepts

Detailed explanations are available in the Concepts Documentation.

Concept Direction Description
Scope ⬇️ Down Context. Parents inject variables inherited by all descendants.
Signals ⬆️ Up Aggregation. Children emit data “frames” that bubble up for summarization.
Weave 🔄 Loop Convergence. The engine runs multiple passes until data stabilizes.

Limitations

Loom operates within the specific boundaries of the Typst runtime. Review the full Limitations Guide for more details.

  • Vertical-Only Flow: Data flows Child -> Parent. Sibling components cannot see each other in the same pass.
  • Stack Depth: Recursion is limited to approximately 50 levels.
  • Opaque Fields: Loom cannot see inside named arguments like figure(caption: [...]).
  • Show Rules: Standard #show rules run after Loom’s logic and cannot transform text into Loom components.

License

MIT License