npm install -D shiki @shikijs/markdown-it
// 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' }));
}
// 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 "Languagetomlnot 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.