typst-batch
A Typst → HTML batch compilation library with shared global resources.
This library was extracted from tola-ssg, a Typst-based static site generator. It is specifically designed for Typst → HTML workflows and may not be generic enough for all use cases — but feel free to give it a try!
Of course, it also works well for single document compilation when you need features like virtual file injection or friendly helper functions.
If you need:
- PDF output → Use typst directly
- Single file compilation → The official
typst-cliis simpler (unless you need VFS features)
Features
- Shared fonts: Loaded once (~100ms saved per compilation)
- Cached packages: Downloaded once from Typst registry
- Incremental builds: Fingerprint-based file cache invalidation
- Structured diagnostics: Rich error messages with source locations
- Virtual file system: Inject dynamic content without physical files
- Metadata extraction: Query labeled values from compiled documents
Installation
[]
= "0.1"
# Optional: Enable colored diagnostics (requires `colored` crate)
# typst-batch = { version = "0.1", features = ["colored-diagnostics"] }
Features
| Feature | Description |
|---|---|
colored-diagnostics |
Enable ANSI colored output via colored crate |
Quick Start
use *;
use Path;
API Overview
Compilation
| Function | Description |
|---|---|
compile_html |
Compile to HTML bytes |
compile_html_with_metadata |
Compile with metadata extraction |
compile_document |
Get HtmlDocument for further processing |
Metadata Extraction
Extract structured data from your Typst documents using labels:
In your .typ file:
#metadata((
title: "My Blog Post",
date: "2024-01-01",
tags: ("rust", "typst"),
)) <post-meta>
= My Blog Post
This is the content...
In Rust:
use compile_html_with_metadata;
use Path;
let result = compile_html_with_metadata?;
// Access metadata as serde_json::Value
if let Some = &result.metadata
Query multiple labels at once:
use ;
let doc_result = compile_document?;
let metadata_map = query_metadata_map;
if let Some = metadata_map.get
if let Some = metadata_map.get
Document Inputs (sys.inputs)
Pass runtime data to Typst documents via sys.inputs:
In Rust:
use compile_html_with_inputs;
use Path;
// Simple key-value pairs
let result = compile_html_with_inputs?;
In your .typ file:
#let title = sys.inputs.at("title", default: "Untitled")
#let author = sys.inputs.at("author", default: "Unknown")
= #title
by _#author_
Performance Considerations
Using sys.inputs creates a new library instance per compilation, which
has a small performance overhead. For batch compilation of many documents:
Recommended for static/global data: Use VFS (Virtual File System) to inject shared data as files. This allows all compilations to share the global library:
use ;
let mut vfs = new;
vfs.insert;
set_virtual_fs;
// All compilations share the global library - best performance
let result = compile_html?;
Use sys.inputs for: Build-time variables, CLI arguments, or truly
document-specific data that can't be pre-computed as VFS files.
Low-level API with SystemWorld:
use ;
use ;
// Builder pattern
let world = new
.with_inputs;
// Or with pre-built Dict
let mut inputs = new;
inputs.insert;
let world = new
.with_inputs_dict;
// Then compile with typst::compile(&world)
Performance Note: Using sys.inputs creates a new library instance per
compilation, bypassing the shared global library. For batch compilation without
inputs, use the standard compile_html() which shares resources.
Virtual File System
The VFS allows you to inject dynamic content that doesn't exist on disk, enabling flexibility and extensibility that would be difficult to achieve in Typst alone.
Use cases:
- Inject computed data (post lists, site config, build timestamps)
- Provide per-document context without modifying source files
- Implement template inheritance patterns
- Share data between documents without file I/O
Compatibility Note: VFS is a non-standard extension. Documents using virtual
files won't compile with standard typst-cli. Consider this trade-off for your use case.
Virtual files are accessible in Typst via #json(), #read(), #yaml(), etc.
Simple usage with MapVirtualFS:
use ;
let mut vfs = new;
// Inject JSON data
vfs.insert;
// Inject computed data
let posts_json = to_string?;
vfs.insert;
// Register globally (call once at startup)
set_virtual_fs;
In your .typ file:
#let site = json("/_data/site.json")
#let posts = json("/_data/posts.json")
= #site.title
#for post in posts [
- #link(post.url)[#post.title]
]
Custom VFS implementation:
use ;
use Path;
set_virtual_fs;
Chained VFS - combine multiple providers:
use ;
use Path;
/// A VFS that chains multiple providers, trying each in order.
// Usage: combine site config + per-document data
let vfs = new
.add
.add
.add;
set_virtual_fs;
Diagnostics
Format compilation errors and warnings with source context:
use ;
use Path;
let path = new;
let root = new;
let world = new;
let result = compile_html?;
// Use trait methods on diagnostics
if result.diagnostics.has_errors
// Get summary
let summary = result.diagnostics.summary;
println!; // "2 errors, 1 warning"
// Or get raw counts
let = result.diagnostics.counts;
// Format diagnostics (default: colored with rich snippets)
let formatted = result.diagnostics.format;
eprintln!;
// Format with custom options
let options = DiagnosticOptions ;
let formatted = result.diagnostics.format_with;
// Short style for CI/IDE (file:line:col: message)
let short = result.diagnostics.format_with;
// Filter out unwanted diagnostics
use DiagnosticFilter;
// Filter out HTML export warnings (shorthand)
let filtered = result.diagnostics.filter_html_warnings;
// Or use the general filter API for more control
let filtered = result.diagnostics.filter_out;
// Available filters:
// - DiagnosticFilter::HtmlExport - HTML export development warning
// - DiagnosticFilter::ExternalPackages - Warnings from external packages (@preview/...)
// - DiagnosticFilter::AllWarnings - All warnings (keep only errors)
// - DiagnosticFilter::MessageContains(s) - Custom filter by message text
Full Customization with Structured Data:
For complete control over rendering (JSON, HTML, IDE integration, etc.),
use .resolve() to get structured DiagnosticInfo:
use ;
// Resolve all diagnostics to structured data
for info in result.diagnostics.resolve
The DiagnosticInfo struct contains:
severity:Severity::ErrororSeverity::Warningmessage: The diagnostic messagepath: Source file path (optional)line,column: Location info (optional)source_lines: Vec ofSourceLinewith line number, text, and highlight rangehints: Vec of hint stringstraces: Vec ofTraceInfowith call stack details
Font Configuration
use ;
use Path;
// Option 1: Simple initialization with system fonts
get_fonts;
// Option 2: With custom font directories
get_fonts;
// Option 3: Detailed configuration
let options = new
.with_system_fonts // Include system fonts
.with_custom_paths;
init_fonts_with_options;
// Check loaded fonts
if let Some = font_count
Typst Access
For advanced use cases, access the full typst ecosystem:
// Access any typst type via the re-exported crate
use ;
use ;
use ;
use ;
// Or use the full crates
use typst_html;
use typst_kit;
Requirements
- Typst 0.14.1
License
MIT