Ports of the most-used unified (remark/rehype) plugins to Sätteri, the Rust Markdown/MDX engine behind Astro 7.
Sätteri does not run remark or rehype plugins — it has its own AST, parser and serializer. Upgrading for the speed means leaving your plugin stack behind. These are behaviour-compatible replacements, each tested against the output of the plugin it replaces.
| Package | Replaces | What it does |
|---|---|---|
satteri-slug |
rehype-slug |
Adds id to every heading, via the same github-slugger |
satteri-autolink-headings |
rehype-autolink-headings |
Adds anchor links to headings |
satteri-katex |
rehype-katex |
Renders math with KaTeX |
Live example, built by Astro 7 in CI: https://ashish-codejourney.github.io/satteri-plugins (source)
Much of what needed a plugin under unified is built into Sätteri. Check here before looking for a port.
| Plugin | Replacement |
|---|---|
remark-gfm |
features.gfm (on by default) — tables, footnotes, strikethrough, tasklists, autolinks |
remark-frontmatter |
features.frontmatter (on by default) — YAML and TOML |
remark-math |
features.math — parsing only, pair with satteri-katex to render |
remark-directive |
features.directive |
remark-smartypants |
features.smartPunctuation |
remark-parse, remark-stringify, remark-rehype, rehype-parse, rehype-stringify |
The pipeline itself |
remark-wiki-link |
features.wikilinks |
remark-sup, remark-sub |
features.superscript, features.subscript |
remark-definition-list |
features.definitionList |
Custom {#id} heading syntax |
features.headingAttributes |
Sätteri does not generate heading ids on its own — that is what
satteri-slug is for.
| Plugin | Port |
|---|---|
rehype-slug |
satteri-slug |
rehype-autolink-headings |
satteri-autolink-headings |
rehype-katex |
satteri-katex |
| Plugin | Use instead |
|---|---|
rehype-external-links |
satteri-external-links |
remark-emoji |
satteri-emoji |
remark-toc |
@bhdouglass/satteri-toc, pretty-toc |
rehype-mermaid |
satteri-beautiful-mermaid, @xingwangzhe/satteri-mermaid |
rehype-github-alerts, remark-github-blockquote-alert |
satteri-callouts |
remark-breaks |
@minittupoyo/satteri-breaks |
| Code highlighting | satteri-expressive-code |
| MDX auto-imports | @bhdouglass/satteri-auto-imports, @xsynaptic/satteri-auto-import |
rehype-sanitize, rehype-mathjax, rehype-highlight, remark-validate-links, and a
unified compatibility shim. Contributions welcome.
Two things about Astro's Sätteri processor are worth knowing before you wire anything up, because both are invisible until your output is wrong. Astro composes the plugin list as:
[ syntax highlighter ] → [ your hastPlugins ] → [ image marker ] → [ heading ids ]
- Pass
satteri-katextomdastPlugins, nothastPlugins. The highlighter runs first and, on HAST, display math is still a<pre><code>— so it gets highlighted as aplaintextcode block and never reaches a HAST math plugin. satteri-slugis a prerequisite forsatteri-autolink-headings. Astro assigns heading ids after your plugins, so withoutsatteri-slugthe headings have noidyet and every anchor is skipped.satteri()accepts onlymdastPlugins,hastPluginsandfeatures. Anything else you pass it —shikiConfig,gfm,smartypants— is silently dropped. Those belong one level up, onmarkdown, alongsideprocessor.
export default defineConfig({
markdown: {
shikiConfig: { themes: { light: "github-light", dark: "github-dark" } },
processor: satteri({
features: { math: true },
mdastPlugins: [satteriKatex()],
hastPlugins: [satteriSlug(), satteriAutolinkHeadings()],
}),
},
});Every package follows the same method:
- Characterise. Run the original plugin over a fixture corpus and capture its HTML. That output is the specification — not the original's source code.
- Test first. Each behaviour gets a failing test asserting the captured HTML before any implementation exists.
- Mutation-test. Deliberately break the implementation and confirm a test catches it. Anything that survives is a missing test or dead code, and gets fixed either way.
- Verify in a real build.
pnpm test:e2ebuildsexamples/astro7-sitewith Astro 7 and asserts on the HTML on disk. Both Astro ordering problems above were found this way, after the unit tests were already green. - Document divergences. Where Sätteri's model makes exact parity impossible, each README says so under Differences from ….
See CONTRIBUTING.md. Ports are held to the method above — a PR that skips the characterisation or mutation steps will be asked to redo them.
pnpm install
pnpm test # unit tests
pnpm -r typecheck
pnpm -r build
pnpm test:e2e # builds the example site with Astro 7 and asserts on its HTMLMIT © Ashish Vaghela