A fully client-side Typst editor written in Rust and compiled to WebAssembly — with live preview, syntax highlighting, and autocomplete.
Runs in the browser or as a native desktop app, from a single codebase.

Compilation, storage, and rendering all happen in the browser; there is no backend. The same crate ships as a desktop application through Tauri, so the web and native builds share one source tree.
Editing
Tab / Shift+Tab block indentation.typst-ide (functions, labels, citations, packages). Type to filter, or trigger explicitly with Ctrl+Space.Ctrl+B / Ctrl+I (bold/italic), Ctrl+S (save), Ctrl+F (find & replace).Documents
.typ files for #include / #import, with multi-page rendering.refs.yml.001–999).@preview packages — #import "@preview/…" fetches from packages.typst.org and caches the tarball in IndexedDB.Navigation & sharing
localStorage automatically.Deployment
| Area | Technology |
|---|---|
| Language | Rust (nightly) |
| UI framework | Leptos 0.8 (CSR) |
| Typesetting | typst-as-lib (wraps the official typst 0.13 crate) |
| Styling | Tailwind CSS 4 + daisyUI 5 |
| Icons | Iconify (Lucide) |
| Web bundler | Trunk |
| Desktop runtime | Tauri 2.8 |
wasm32-unknown-unknown targetrustup toolchain install nightly --allow-downgrade
rustup target add wasm32-unknown-unknown
cargo install trunk
npm install # build-time CSS/icon dependencies
trunk serve --open # the pre_build hook compiles the CSS automatically
This compiles the crate to WASM, serves it at http://127.0.0.1:1420, and reloads on
change.
npm install # first time only
npm run tauri:dev # or: cargo tauri dev
Trunk and the Tauri backend start together, opening a native window with hot reload for both layers.
trunk build --release # standard release
trunk build --config Trunk-release.toml # GitHub Pages (repo sub-path)
Both write to dist/, ready for any static host. The included GitHub Actions workflow
(.github/workflows/deploy.yml) builds with
Trunk-release.toml and publishes the dist/ artifact to GitHub Pages on push to the
default branch. (Set Settings → Pages → Source to GitHub Actions.)
npm run tauri:build # or: cargo tauri build
Installers are written to src-tauri/target/release/bundle/:
| Platform | Artifacts |
|---|---|
| Linux | .deb, .AppImage, .rpm |
| Windows | .msi, .exe |
| macOS | .dmg, .app |
Bundle size is roughly 85–100 MB, including the Tauri runtime and the WASM payload.
Type Typst markup in the Source panel on the left and watch the Preview panel on
the right; work is saved to localStorage continuously. Ctrl+B / Ctrl+I wrap the
selection in bold/italic, Tab / Shift+Tab indent or outdent selected lines, Ctrl+S
saves, and Ctrl+F opens find & replace. Brackets and quotes auto-pair, and native
undo/redo (Ctrl+Z / Ctrl+Y) is preserved.
A completion dropdown appears as you type, or on demand with Ctrl+Space. It suggests
functions, parameters, labels, citations from your bibliography, and @preview package
names. Navigate with the arrow keys and accept with Enter or Tab.

Click New in the header and choose Blank, Article, or IEEE. This replaces the current project; the IEEE template also loads a sample bibliography.
@preview packagesImport community packages directly:
#import "@preview/cetz:0.3.4": canvas, draw
On first use the package is downloaded from packages.typst.org (a Downloading…
indicator appears in the header) and cached in IndexedDB, so later loads need no network.
Use the zoom controls (−/100%/+) in the preview header, follow the page indicator (p. N / M), and click any text in the preview to jump the editor caret to the matching source location.
Click Share to copy a URL with the document encoded in its fragment. Opening that URL loads the snapshot once; afterwards the locally auto-saved project takes over.
Open the Images gallery from the toolbar and upload a file (PNG, JPG, GIF, WebP, SVG). Each image is assigned a sequential three-digit ID; copy it and reference it from your document:
#figure(
image("001.png"),
caption: [Your image caption],
)
Images are stored in IndexedDB, support drag-and-drop upload, and can be previewed, copied by ID, or deleted from the gallery.
Open the Bibliography manager and edit references in Hayagriva YAML:
netwok2020:
type: article
title: "The Challenges of Scientific Typesetting"
author: ["Network, A.", "Smith, B."]
date: 2020
journal: "Journal of Academic Publishing"
Then cite them in your document:
According to @netwok2020, this is correct.
= References
#bibliography("refs.yml")
The bibliography is stored in localStorage and registered as a virtual refs.yml via
the compiler's file resolver. All Hayagriva entry types are supported.
Use the PDF or SVG buttons in the toolbar to download the current document.
wasm-typst-studio-rs/
├── src/ # Leptos app (shared by web and desktop)
│ ├── lib.rs # App component, state, persistence
│ ├── main.rs # WASM entry point
│ ├── compiler/
│ │ ├── typst.rs # Persistent engine, dynamic file resolver, compile/click APIs
│ │ ├── ide.rs # Minimal IdeWorld for typst-ide autocomplete and jump
│ │ ├── packages.rs # @preview package fetch + tar.gz extraction
│ │ └── mod.rs
│ ├── components/
│ │ ├── editor.rs # Textarea + overlay editor, gutter, shortcuts, autocomplete UI
│ │ ├── preview.rs # SVG preview, zoom, page indicator, click-to-jump
│ │ ├── image_gallery.rs
│ │ └── mod.rs
│ └── utils/
│ ├── highlight.rs # Syntax highlighting
│ ├── editing.rs # Undo-safe edits, indent/find helpers, UTF-16 ↔ byte mapping
│ ├── image_manager.rs # Image management with sequential IDs
│ ├── image_storage.rs # IndexedDB image storage
│ ├── package_storage.rs # IndexedDB cache for @preview tarballs
│ ├── project.rs # Multi-file project (de)serialization
│ ├── share.rs # Shareable-link URL fragment encode/decode
│ ├── download.rs # File download helper
│ └── mod.rs
├── src-tauri/ # Tauri backend (desktop only)
├── examples/ # Default document, IEEE example, bibliography
├── templates/ # Bundled templates for the New… picker
├── tests/e2e/ # Playwright smoke suite (dev tooling, not in CI)
├── index.html # Web HTML template
├── tailwind.css # Tailwind CSS 4 source (CSS-first)
├── Trunk.toml # Trunk config (development)
├── Trunk-release.toml # Trunk config (production / GitHub Pages)
└── rust-toolchain.toml # Pinned toolchain
[build]
target = "index.html"
dist = "dist"
public_url = "/"
[serve]
port = 1420
[build]
target = "index.html"
dist = "dist"
public_url = "/wasm-typst-studio-rs/" # must match the GitHub Pages repo name
release = true
minify = "always"
[toolchain]
channel = "nightly"
targets = ["wasm32-unknown-unknown"]
Web
@preview packages require network on first use of each package, and only the
@preview namespace is served. Local or custom packages are not supported.Desktop
Contributions are welcome.
bash
cargo clippy --target wasm32-unknown-unknown -- -D warnings
cargo test
trunk build
Please follow the existing code style (cargo fmt), keep dependencies minimal, and record
user-visible changes in CHANGELOG.md.
Released under the MIT License.
Project docs
Typst
Rust, WASM & Tauri
$ claude mcp add wasm-typst-studio-rs \
-- python -m otcore.mcp_server <graph>