Migrating from Shiki
Keep a loaded highlighter for the lifetime of your CMS worker or site build. That reuse is the workload behind Ferriki's two-host measurements: 3.3× faster than Shiki/WASM on the recorded Linux corpus and 4.1× on macOS, with identical HTML. Keep the default regex prefilter for repeated work; factory options cover the alternative for short-lived highlighters.
Ferriki follows the direct API shape of Shiki v4.4.3 for a tested, HTML-focused subset and runs it on a native engine. For HTML highlighting, moving over is usually an import:
- import { createHighlighter } from "shiki";
+ import { createHighlighter } from "@ferriki/core";
const highlighter = await createHighlighter({
langs: ["typescript"],
themes: ["nord"],
});
const html = highlighter.codeToHtml(source, { lang: "typescript", theme: "nord" });
What stays
| Shiki surface | In Ferriki |
|---|---|
codeToHtml | Supported call shape for rendered HTML |
| Reusable highlighters and the singleton | Same factories; dispose long-lived instances explicitly |
| Standard languages, themes and aliases | bundledLanguages, bundledThemes, bundledLanguagesAlias |
| Custom TextMate grammars and themes | JSON-shaped registrations validated before they reach the engine |
themes map and defaultColor: false | Supported for light and dark output |
transformers and decorations | Supported in the JavaScript facade; callbacks stay outside the native boundary |
| CSS class output | Ferriki extension: styleMode: 'classes' or codeToHtmlWithCss() |
What changes
| Shiki surface | In Ferriki | What to do |
|---|---|---|
engine, createOnigurumaEngine, createJavaScriptRegexEngine | Not in Ferriki | Delete the engine option; Ferriki has one native engine |
loadWasm, wasmBinary | Not in Ferriki | Install the platform package instead |
codeToHast, hastToHtml, codeToTokens* | Not in Ferriki | Use the HTML output; Rust callers get tokens from the ferriki crate |
| Standard grammar and theme payloads | Downloaded on demand | Async factory, load and one-shot calls fetch and verify missing release assets; configure the asset cache or mirror for offline use. |
| Browser runtime | Not supported | Highlight in Node.js at build or render time, or with the Vite build-time macros |
lang: "ansi" with escape sequences | Rejected | Strip or parse terminal output first |
rehype, markdown-it and other adapters | Not part of Ferriki | Keep the adapter in your Markdown layer and use an HTML integration |
"Shiki-compatible" means a tested subset: the baseline and the exclusions are checked by machine against a pinned mirror of Shiki's own test suite (see Compatibility).
The Node API returns rendered HTML and CSS. If your integration consumes HAST
or token arrays, that part needs another path: keep it on Shiki, or read tokens
from the ferriki crate in Rust.
Before you switch
- Confirm that every deployment target can install a Ferriki native binary.
- Run your build from an installed package in CI, not from a checkout.
- Keep the Shiki implementation behind the same interface until the switch is done, so a target without a binary can fail the deployment explicitly.
The full guide, including asset loading, output-shape differences, and downstream adapter boundaries, is docs/migrations/shiki-to-ferriki.md.
Code examples inside components can move to build time: @ferriki/vite
prepares code() and <Code /> macros during a Vite 8 build. See
Build-time macros.