Automating Document Generation with Typst
Introduction
I like document workflows that behave more like software than office files.
That usually means a few things:
- the source should be easy to review in Git
- layout should live in a reusable template
- data should be separate from presentation
- generating the final PDF should be a command, not a manual ritual
That mindset is what pushed me toward Typst.
I wanted a cleaner way to generate polished documents such as reports, proposals, and internal summaries without reopening the same file, adjusting spacing by hand, and wondering whether I had missed a small edit before exporting the final PDF. Typst turned out to be a good fit because it gives you strong layout control, a readable syntax, and a fast feedback loop that feels much closer to working with code than with a traditional document editor.
The Problem with Manual Document Work
Manual document generation usually breaks down in predictable ways.
The content starts in one place, the latest numbers live somewhere else, and the final formatting happens in a file that gradually becomes harder to trust. Even when the document itself is not complicated, the process around it often is:
- someone copies data from a spreadsheet or dashboard
- someone else updates a date, heading, or customer-specific section
- formatting shifts in subtle ways
- exported PDFs end up with inconsistent spacing, naming, or metadata
The real issue is not just that this work is repetitive. It is that the repetition does not add value. Time gets spent on moving content around instead of improving the document itself.
I wanted the workflow to become:
- update the source data
- run one command
- review the generated PDF
- ship it
That is a much healthier shape for a recurring task.
Why Typst Was a Good Fit
Typst sits in a nice middle ground.
It gives you much better structure and reproducibility than editing documents manually, but it feels lighter and more approachable than older typesetting workflows. A few things stood out immediately:
- the syntax is compact and readable
- templates are easy to keep in version control
- compile times are fast enough to support iteration
- layout logic can be expressed without building a large custom rendering system
I also liked that Typst encourages a clean mental model: content comes from data, presentation comes from the template, and the output is deterministic. That makes automation much easier because each part of the pipeline has a clear responsibility.
Separating Data from Layout
The first useful design decision was to stop treating the document as a single blob.
Instead of editing the final file directly, I moved the variable content into structured input data and kept the layout in a Typst template. That one change made the whole process easier to reason about.
For example, a report can be expressed as JSON:
{
"title": "Monthly Operations Report",
"generated_at": "2026-03-20",
"metrics": [
{ "label": "Deployments", "value": "27" },
{ "label": "Success rate", "value": "99.2%" },
{ "label": "Incidents closed", "value": "14" }
],
"notes": "Most issues came from dependency updates, not application changes."
}
Then the Typst template can focus on presentation:
#set page(margin: 24mm)
#set text(font: "IBM Plex Sans", size: 10pt)
#let data = json("data/report.json")
= #data.title
_Generated on #data.generated_at_
== Summary
#for item in data.metrics [
- *#item.label:* #item.value
]
== Notes
#data.notes
This looks simple, but it changes the workflow in an important way. Once the variable content lives in a structured format, document generation becomes scriptable. The same template can render many outputs, and the document stops being something you hand-edit from scratch every time.
Building the Automation Around It
After that, the rest of the system became fairly straightforward.
The pipeline I ended up with looks like this:
- collect or prepare the source data
- validate the fields that the template expects
- write the input data to a known location
- compile the Typst template into a PDF
- store or publish the generated artifact
At the command line, the core step is pleasantly boring:
mkdir -p build
typst compile templates/report.typ build/monthly-operations-report.pdf
That is exactly what I want from automation. The interesting work should happen in the data and template layers. The final rendering step should be mechanical and repeatable.
In practice, I wrapped that command in a small script so the pipeline could do a few extra things reliably:
- create predictable output names
- fail early if required fields are missing
- keep generated files out of the source template directories
- make local runs and CI runs behave the same way
Once that wrapper existed, generating a document stopped depending on memory. I did not need to remember which folder to export from, which styles to reapply, or which filename pattern to use. The pipeline handled it every time.
Template Design Matters More Than the Render Command
The command that renders a PDF is the easy part. The harder part is designing a template that is flexible without becoming opaque.
What helped most was keeping the Typst template opinionated:
- define one clear page layout
- centralize typography and spacing decisions
- keep conditional sections explicit
- avoid mixing data cleanup with presentation logic
If the template starts compensating for inconsistent input data, it becomes harder to maintain. I found it much cleaner to validate and normalize the data before the Typst render step, then let the template assume the inputs are already sane.
That division pays off quickly. When a document changes, I can usually tell whether the change belongs in the data model, the template, or the automation script. That reduces the amount of guessing during maintenance.
What Worked Well
A few benefits showed up almost immediately.
First, the output became more consistent. The generated PDFs followed the same layout every time, which removed the small formatting drift that creeps in with manual editing.
Second, review became easier. Instead of trying to compare two exported PDFs line by line, I could review the source data and template changes in Git. That made it much easier to see what actually changed.
Third, recurring documents became much cheaper to produce. Once the template and automation were in place, creating the next document mostly meant updating inputs and rerunning the pipeline.
The broader win was confidence. I trusted the workflow more because it was explicit, versioned, and reproducible.
Challenges and Tradeoffs
The workflow is better, but it is not free.
The main tradeoff is that you need to think about structure up front. If every document is truly one-off and highly custom, automation can feel heavier than manual editing. Typst shines when there is a pattern worth encoding.
There were also a few practical concerns to manage:
- fonts and asset paths need to be predictable across environments
- templates should fail clearly when data is incomplete
- generated files should not be mistaken for source files
- reviewers may need a small adjustment period if they are used to editing documents directly
None of those issues are deal breakers, but they do matter. Document automation works best when the workflow is treated like a real piece of software, not just a shortcut script.
What I Would Improve Next Time
If I were extending this setup further, I would invest in three things.
The first would be stronger validation before render. A small schema check on the input data would make failures faster and more obvious.
The second would be preview automation in CI. For document-heavy changes, it would be useful to generate the PDF automatically in pull requests so reviewers can inspect the final output without running the pipeline locally.
The third would be a better publishing layer. Once documents are generated reliably, the next logical step is making delivery just as consistent, whether that means attaching artifacts to a release, uploading to cloud storage, or feeding another internal system.
Closing Thoughts
What I liked most about Typst was not just the syntax or the PDF output. It was the way it encouraged a better workflow.
Instead of treating document generation as a messy final step, Typst made it easy to treat it as a repeatable build process. Templates became versioned assets. Data became explicit input. Rendering became a dependable command.
That is the real value of this kind of automation. It removes manual formatting work, but more importantly, it makes document generation easier to trust.
Written by
Ashesh Nepal