Open-source project
bruits/satteri avatar
bruits/satteri

Sätteri: Rust-speed Markdown and MDX with JavaScript plugins

High-performance Markdown and MDX processing for the JavaScript ecosystem.

1,262 stars34 forksRustMIT

At a glance

What is it?
Sätteri is a Rust plus TypeScript monorepo that parses and compiles Markdown and MDX in Rust while running your plugins in JavaScript. It is aimed at build-tool authors and site engineers who want unified-style extensibility without the JavaScript parser in the hot path.
Who is it for?
Adopt Sätteri if you are building a Vite or MDX pipeline and want the parser and compiler in Rust while keeping plugin code in JavaScript, and if you can accept that most guidance lives in the online docs rather than the README. Do not adopt it if you need a single-language toolchain, or if you depend on a plugin that reaches deep into unified internals.
Can I use it commercially?
Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 9 days ago.
What is it written in?
Mainly Rust, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on September 25, 2026, and from our analysis. They are not legal advice.

Editorial analysis

The gap Sätteri fills between remark-style plugins and a Rust parser

Markdown pipelines in the JavaScript ecosystem have a familiar shape: a parser produces a syntax tree, plugins walk that tree, and a compiler turns it into HTML or JavaScript. The unified ecosystem, including remark and rehype, is the reference implementation of that shape, and the README credits it directly. The cost is that parsing and compilation happen in JavaScript, which matters when a build processes thousands of files.

Sätteri splits the pipeline across a language boundary. The README states the design in one line: it parses and compiles in Rust and runs your plugins in JavaScript. The intended audience is therefore narrow and specific. If you write a Vite site or an MDX-based documentation build and you already have plugins written against MDAST or HAST node types, Sätteri is trying to keep those plugins and move the surrounding work into Rust. If you have no plugin code and no build pipeline, there is nothing here for you to adopt.

How the Rust pipeline and the JavaScript plugin layer fit together

The repository is a monorepo with two halves. The Rust half contains crates for the pipeline itself (satteri), the tree types (satteri-ast, which holds MDAST and HAST node types, codecs, tree operations and conversion), the plugin trait and runner (satteri-plugin-api), the NAPI bindings that expose the pipeline to JavaScript (satteri-napi-binding), and two forks: satteri-pulldown-cmark, a CommonMark parser with MDX extension support forked from pulldown-cmark, and satteri-mdxjs-rs, an MDX-to-JavaScript compiler forked from mdxjs-rs and adapted for OXC.

The npm half is the surface most users touch: the satteri package provides the TypeScript layer with the plugin API and top-level functions, satteri-expressive-code is a HAST plugin that renders code blocks with Expressive Code, and vite-plugin-satteri lets a Vite build import .md and .mdx files. The NAPI binding is the seam between the two halves, so the data flow is Rust parse, Rust tree operations, JavaScript plugin hooks, Rust compile. The README also credits Lightning CSS for its optimized JavaScript Visitor API, which is the same interop pattern applied to CSS.

The examples directory shows the three entry styles the project expects you to write: examples/basic.ts, examples/hast-plugin.ts and examples/mdast-plugin.ts. That naming is the clearest signal of the intended plugin model: you choose whether your plugin operates on the Markdown tree or the HTML tree, and the Rust side handles the conversion between them through satteri-ast.

Installing Sätteri and converting your first Markdown file

The README does not contain install commands. It points to satteri.bruits.org/docs/installation/ for installation instructions, satteri.bruits.org/docs/quick-start/ for usage examples, and satteri.bruits.org/docs/entry-points/ for the API reference. There is also an online playground at satteri.bruits.org/playground if you want to see the output before installing anything.

What the repository does confirm is the package names. The npm packages are satteri, satteri-expressive-code and vite-plugin-satteri, and the root package.json pins pnpm as the package manager, so the workspace is built with pnpm rather than npm or yarn. A first use therefore starts by installing the TypeScript layer, and for a Vite project the plugin package is the shorter path because it registers the .md and .mdx import handling for you. The exact install commands are on the documentation site rather than in the README, so take them from satteri.bruits.org/docs/installation/.

The recent release history is concentrated on the Vite plugin: vite-plugin-satteri-v0.3.5, v0.3.4 and v0.3.3 were all tagged on 2026-08-19. If you are evaluating stability, that is where the churn currently sits, not in the crates.

If you want to work from the repository itself, the Rust side is a Cargo workspace. The release profile in Cargo.toml uses lto = "fat", codegen-units = 1 and panic = "abort", which tells you the maintainers are optimizing for binary size and throughput rather than build time. Expect slow release builds on a large machine and plan accordingly.

The feature-flag trap in the Cargo workspace

The most interesting detail in the repository is a comment inside Cargo.toml. It explains that default-features = false is declared in the workspace dependency table for satteri-ast, satteri-plugin-api and satteri-pulldown-cmark because a per-crate default-features = false is ignored for workspace = true dependencies unless it is also declared in that table. The comment goes further: this is what lets consumers drop the mdx default and, for pulldown-cmark, the whole oxc stack.

That is a real constraint, and it cuts both ways. The upside is that a Rust consumer who only needs CommonMark can avoid pulling in OXC, the JavaScript parser and compiler used for MDX compilation. The downside is that feature resolution now depends on a workspace-level declaration that is easy to break. If you fork the workspace or vendor a crate, the flags are not where a reader would first look. The comment exists precisely because the maintainers hit this.

For JavaScript users this never surfaces, since the npm packages ship prebuilt bindings. For anyone consuming the crates directly, it is the first thing to read in Cargo.toml before changing dependencies.

What the README does not tell you

The README is a package index, not a guide. It lists crates and npm packages with links to per-package READMEs and to the documentation site, and it credits the upstream projects it builds on. It does not document installation, it does not show a plugin example, and it does not describe the plugin API signature. Those live at satteri.bruits.org/docs/.

Two other gaps are worth naming. First, the README does not document rollback or version compatibility between the Rust crates and the npm packages. The versions in Cargo.toml (satteri-ast 0.5.3, satteri-plugin-api 0.5.3, satteri-pulldown-cmark 0.6.3, satteri-mdxjs 0.3.13, satteri-arena 0.3.1) are all pre-1.0, and the npm layer is versioned separately. Nothing in the repository states which crate version a given satteri npm release bundles. Second, the README does not say how a JavaScript plugin is registered with the Rust runner, beyond the existence of satteri-plugin-api and the examples directory. If your adoption decision hinges on the plugin registration API, the docs site is the only place to check.

There is also a naming problem you should be aware of before searching for help. The project name collides with Sateri, a fibre company, and search results for the project are polluted by results about lyocell, nonwovens and sodium sulphate. Searching for "satteri plugins" or "satteri mdx" is more useful than searching the bare name.

Sätteri against unified, remark and rehype

The honest alternative is the unified ecosystem the README itself credits: remark for Markdown, rehype for HTML, and the unified pipeline that connects them. Both approaches produce MDAST and HAST trees and both let you write plugins against them, which is why Sätteri's examples are named after the tree types.

The difference is where the work happens. In unified, the parser, the tree transforms and the compiler are all JavaScript, and plugins are ordinary functions in the same runtime. In Sätteri, parsing and compilation are Rust and only the plugin hooks cross into JavaScript through NAPI bindings. That boundary is the whole product. It buys you a faster parser and compiler, and it costs you a serialization step at every plugin boundary, plus a build that has to ship or compile a native binary.

A second difference is the MDX compiler. Sätteri forks mdxjs-rs and adapts it for OXC, so MDX-to-JavaScript output goes through a different JavaScript toolchain than the upstream mdxjs-rs. If you have downstream tooling that assumes a specific output shape, that fork is a compatibility question you have to test rather than assume.

A third option, if you only need CommonMark and no plugins, is the pulldown-cmark crate directly. Sätteri's own parser is a fork of it with MDX extensions. If you do not need the plugin layer, the fork is not the shortest path.

Maintenance, licensing and what a fork-based dependency costs you

The repository is not archived, and the last push was on 2026-08-19, one month before this writing. The three most recent releases are all vite-plugin-satteri tags from that same day, which suggests the Vite integration is the active edge of the project right now. The Rust crates carry their own versions and are not part of that release burst.

The licence is MIT, declared in the repository LICENSE file and in the root package.json. MIT is permissive, so the practical implication for adopters is attribution rather than copyleft obligations, but the forks matter more than the licence does. satteri-pulldown-cmark and satteri-mdxjs-rs are forks, and satteri-mdxjs-rs is adapted for OXC. Upstream fixes in pulldown-cmark or mdxjs-rs do not arrive automatically; someone has to port them. That is a maintenance cost you inherit, and it is the main structural risk in the project.

Upgrade cost is uneven. The npm packages move together with the Vite plugin releases. The crates move on their own schedule, and because the workspace feature flags for mdx and std are declared centrally in Cargo.toml, a dependency bump can change what gets compiled in ways that only show up at build time. Pin your crate versions and check the feature graph when you upgrade.

Editorial conclusion

Adopt Sätteri if you are building a Vite or MDX pipeline and want the parser and compiler in Rust while keeping plugin code in JavaScript, and if you can accept that most guidance lives in the online docs rather than the README. Do not adopt it if you need a single-language toolchain, or if you depend on a plugin that reaches deep into unified internals. Before committing, verify the entry points at satteri.bruits.org/docs/entry-points/, confirm the plugin API surface against satteri-plugin-api and the satteri npm package, and check whether your MDX output needs OXC-compatible JavaScript, since satteri-mdxjs-rs is a fork of mdxjs-rs adapted for OXC.

Frequently asked questions

Is Sätteri the same as Sateri, the fibre company?

No. Sätteri is an open-source Markdown and MDX processing project from the Bruits collective, hosted at satteri.bruits.org. The name collides in search results with Sateri, a fibre producer, which is unrelated to this repository.

How do I install Sätteri?

The README does not include install commands and points to satteri.bruits.org/docs/installation/ for installation instructions. The npm packages are satteri, satteri-expressive-code and vite-plugin-satteri, and the repository uses pnpm as its package manager.

What is satteri mdx?

Sätteri handles MDX through satteri-mdxjs-rs, an MDX-to-JavaScript compiler forked from mdxjs-rs and adapted for OXC. The vite-plugin-satteri package lets a Vite build import .mdx files directly.

What are Sätteri plugins and how do they work?

Plugins run in JavaScript while parsing and compilation happen in Rust. The satteri npm package provides the plugin API and top-level functions, and the examples directory shows a basic example plus separate MDAST and HAST plugin examples.

Does Sätteri work with Vite?

Yes. The vite-plugin-satteri npm package is a Vite plugin that lets you import .md and .mdx files. Its 0.3.3, 0.3.4 and 0.3.5 releases were all tagged on 2026-08-19.

What licence does Sätteri use?

MIT, declared in the repository LICENSE file and in the root package.json. Note that satteri-pulldown-cmark and satteri-mdxjs-rs are forks of upstream projects, so upstream changes do not arrive automatically.

Official sources

  1. Official documentation
  2. Official README
  3. Project repository
  4. Release notes
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/bruits-satteri.svg)](https://hysenlabs.com/projects/bruits-satteri)