Skip to content

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 surfaceIn Ferriki
codeToHtmlSupported call shape for rendered HTML
Reusable highlighters and the singletonSame factories; dispose long-lived instances explicitly
Standard languages, themes and aliasesbundledLanguages, bundledThemes, bundledLanguagesAlias
Custom TextMate grammars and themesJSON-shaped registrations validated before they reach the engine
themes map and defaultColor: falseSupported for light and dark output
transformers and decorationsSupported in the JavaScript facade; callbacks stay outside the native boundary
CSS class outputFerriki extension: styleMode: 'classes' or codeToHtmlWithCss()

What changes

Shiki surfaceIn FerrikiWhat to do
engine, createOnigurumaEngine, createJavaScriptRegexEngineNot in FerrikiDelete the engine option; Ferriki has one native engine
loadWasm, wasmBinaryNot in FerrikiInstall the platform package instead
codeToHast, hastToHtml, codeToTokens*Not in FerrikiUse the HTML output; Rust callers get tokens from the ferriki crate
Standard grammar and theme payloadsDownloaded on demandAsync factory, load and one-shot calls fetch and verify missing release assets; configure the asset cache or mirror for offline use.
Browser runtimeNot supportedHighlight in Node.js at build or render time, or with the Vite build-time macros
lang: "ansi" with escape sequencesRejectedStrip or parse terminal output first
rehype, markdown-it and other adaptersNot part of FerrikiKeep 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

  1. Confirm that every deployment target can install a Ferriki native binary.
  2. Run your build from an installed package in CI, not from a checkout.
  3. 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.