Highlight code with Shiki in Stati

Build-time syntax highlighting with Shiki, ready before Stati's synchronous markdown.configure runs.

SNP 069 #2B4 Filed stati markdown

shell
npm install -D shiki @shikijs/markdown-it
javascript
// helpers/markdown.js
import { createHighlighterCore } from 'shiki/core';
import { createOnigurumaEngine } from 'shiki/engine/oniguruma';
import { fromHighlighter } from '@shikijs/markdown-it/core';
import githubDark from '@shikijs/themes/github-dark';
import javascript from '@shikijs/langs/javascript';
import shellscript from '@shikijs/langs/shellscript';

// markdown.configure is called synchronously, so the highlighter has to be ready first
const highlighter = await createHighlighterCore({
  themes: [githubDark],
  langs: [javascript, shellscript],
  engine: createOnigurumaEngine(import('shiki/wasm')),
});

export function configureMarkdown(md) {
  md.use(fromHighlighter(highlighter, { theme: 'github-dark', fallbackLanguage: 'text' }));
}
typescript
// stati.config.ts
import { defineConfig } from '@stati/core';
import { configureMarkdown } from './helpers/markdown.js';

export default defineConfig({
  // site and the rest…
  markdown: { configure: configureMarkdown },
});

Stati calls markdown.configure synchronously and doesn't wait for a promise, so the highlighter is created with a top-level await while the module loads. By the time configureMarkdown runs, it's ready. This site is set up the same way.

  • shiki/core, with themes and languages imported one by one, loads only what you list.
  • Without fallbackLanguage, a fence in a language that isn't loaded throws "Language toml not found". With it, the code is printed as plain text.
  • The blocks come out as <pre class="shiki github-dark" style="…"> with the colours inline, so there's no theme CSS to add.

In the Stati docs: Syntax highlighting and Custom configuration.