Select a language...
Select a theme...
Loading WASM module...

arborium

Finding good tree-sitter grammars is hard. In arborium, every grammar:

  • Is generated with tree-sitter 0.25
  • Builds for WASM & native via cargo
  • Has working highlight queries

We provide any C symbols the scanner might rely on. We patched grammars and wrote highlight queries so you don't have to. Enjoy.

Output formats

HTML — custom elements like <a-k> instead of <span class="keyword">. More compact markup. No JavaScript required.

Traditional <span class="keyword">fn</span>
arborium <a-k>fn</a-k>

ANSI — 24-bit true color for terminal applications.

Platforms

macOS, Linux, Windows — tree-sitter handles generating native crates for these platforms. Just add the dependency and go.

WebAssembly — that one's hard. Compiling Rust to WASM with C code that assumes a standard library is tricky. We provide a sysroot that makes this work, enabling Rust-on-the-frontend scenarios like this demo.

Languages

90+ languages included, each behind a feature flag. Enable only what you need, or use all-languages for everything.

Each feature flag comment includes the grammar's license, so you always know what you're shipping.

Theme support

The highlighter supports themes for both HTML and ANSI output.

Bundled themes:

fn main() {
    let x = 42;
    println!("Hello");
}
Catppuccin Mocha
fn main() {
    let x = 42;
    println!("Hello");
}
Catppuccin Macchiato
fn main() {
    let x = 42;
    println!("Hello");
}
Catppuccin Frappe
fn main() {
    let x = 42;
    println!("Hello");
}
Catppuccin Latte
fn main() {
    let x = 42;
    println!("Hello");
}
Tokyo Night
fn main() {
    let x = 42;
    println!("Hello");
}
Dracula
fn main() {
    let x = 42;
    println!("Hello");
}
Monokai Pro
fn main() {
    let x = 42;
    println!("Hello");
}
One Dark
fn main() {
    let x = 42;
    println!("Hello");
}
Nord
fn main() {
    let x = 42;
    println!("Hello");
}
Gruvbox Dark
fn main() {
    let x = 42;
    println!("Hello");
}
Rosé Pine Moon
fn main() {
    let x = 42;
    println!("Hello");
}
Kanagawa Dragon
fn main() {
    let x = 42;
    println!("Hello");
}
Cobalt2
fn main() {
    let x = 42;
    println!("Hello");
}
Zenburn
fn main() {
    let x = 42;
    println!("Hello");
}
Melange Dark
fn main() {
    let x = 42;
    println!("Hello");
}
Monokai Aqua
fn main() {
    let x = 42;
    println!("Hello");
}
Desert256
fn main() {
    let x = 42;
    println!("Hello");
}
GitHub Dark
fn main() {
    let x = 42;
    println!("Hello");
}
GitHub Light
fn main() {
    let x = 42;
    println!("Hello");
}
Gruvbox Light
fn main() {
    let x = 42;
    println!("Hello");
}
Alabaster
fn main() {
    let x = 42;
    println!("Hello");
}
Dayfox
fn main() {
    let x = 42;
    println!("Hello");
}
Melange Light

Custom themes can be defined programmatically using RGB colors and style attributes (bold, italic, underline, strikethrough).

FAQ

Why not highlight.js or Shiki?

Those use regex-based tokenization (TextMate grammars). Regexes can't count brackets, track scope, or understand structure—they just pattern-match.

Tree-sitter actually parses your code into a syntax tree, so it knows that fn is a keyword only in the right context, handles deeply nested structures correctly, and recovers gracefully from syntax errors.

IDEs with LSP support (like rust-analyzer) can do even better with semantic highlighting—they understand types and dependencies across files—but tree-sitter gets you 90% of the way there without needing a full language server.

Why "arborium"?

Arbor is Latin for tree (as in tree-sitter), and -ium denotes a place or collection (like aquarium, arboretum).

It's a place where tree-sitter grammars live.

I have a grammar that's not included. Can you add it?

Yes! Open an issue on the repo with a link to the grammar.

We'll review it and add it if the grammar and highlight queries are in good shape.

Why not use the WASM builds from tree-sitter CLI?

When doing full-stack Rust, it's nice to have exactly the same code on the frontend and the backend.

Rust crates compile to both native and WASM, so you get one dependency that works everywhere.

Why are tree-sitter parsers so large?

Tree-sitter uses table-driven LR parsing. The grammar compiles down to massive state transition tables—every possible parser state and every possible token gets an entry.

These tables are optimized for O(1) lookup speed, not size. A complex grammar like TypeScript can have tens of thousands of states.

The tradeoff is worth it: you get real parsing (not regex hacks) that handles edge cases correctly and recovers gracefully from syntax errors.

Can I use this without Rust / just embed it on my website?

We don't provide a ready-to-embed kit yet, but you can certainly make one.

Could a ready-to-embed kit load grammars on-demand?

I have a wit-based idea for that: see GitHub issue #1.