Skip to content

Zero-JavaScript Syntax Highlighting with Shiki

2 min readshikiperformancemarkdown

Syntax highlighting should cost the user nothing. Shipping a highlighter to the browser means downloading a grammar engine and tokenizing every code block again, on every visit, for content that never changes after deploy.

Highlight once, at build time

The trick is to run Shiki inside the MDX compilation step. The markdown body is compiled to a string of highlighted HTML, and the client receives plain markup:

src/lib/mdx/rehype-shiki.tstypescript
const root = highlighter.codeToHast(source, {
  lang,
  themes: { light: 'github-light', dark: 'github-dark' },
  defaultColor: false,
})

No highlight.js, no WebAssembly grammar, no theme JavaScript. The bundle is zero bytes heavier for the feature.

Dual themes without a re-render

Instead of baking one theme's colors into the HTML, Shiki emits CSS variables for both palettes. Each token carries --shiki-light and --shiki-dark, and a single rule flips them:

src/styles/app.csscss
html.dark .shiki,
html.dark .shiki span {
  color: var(--shiki-dark) !important;
  background-color: var(--shiki-dark-bg) !important;
}

Switching themes is a class toggle on <html> — no re-highlight, no layout shift.

The result is highlighted code that is instant to render and free to switch between light and dark.