Shiki Syntax Highlighter
Adds Shiki for beautiful, accurate syntax highlighting
storybook-addon-shiki
A Storybook addon that replaces Storybook's syntax highlighting with Shiki for beautiful, accurate syntax highlighting — with zero changes to your stories.
- 🌐 Framework-agnostic — works with React, Vue, Angular, Svelte, or any other Storybook framework.
- 📦 Fine-grained bundling — Shiki loads only the languages and themes you need, keeping your bundle lean.
- 🔌 Drop-in — uses runtime DOM patching; no fragile module aliasing or bundler hacks.
- 🛡️ Future-proof — resilient to Storybook internal changes since it operates at the DOM level.
- 🎨 Full transformer support — supports all Shiki transformers (notation highlights, diffs, focus, etc.).
- 🔢 Line numbers — full support for line numbers in code blocks.
- 📋 Copy button — optional copy-to-clipboard functionality.
- ✅ Supports Vite and Webpack 5 builders.
- 🎯 Rich transformers — Built-in support for diffs, highlights, focus, and more
- 💅 BYO CSS — Bring your own styles for the custom transformers (diffs, focusing, highlighting)
Motivation
This addon directly addresses storybookjs/storybook#29160. Storybook's default syntax highlighting relies on Prism.js which contributes significantly to bundle size.
By using a runtime DOM patching approach, this addon is also future-proof and won't break when Storybook changes its internal module structure.
Installation
pnpm add -D @lukethacoder/storybook-addon-shiki shiki
# or
npm install --save-dev @lukethacoder/storybook-addon-shiki shiki
# or
yarn add -D @lukethacoder/storybook-addon-shiki shiki
Peer dependency:
shiki >= 1.0.0is required and must be installed separately. This keeps you in control of the Shiki version.
View on npm
Setup
Register the addon in .storybook/main.ts:
// .storybook/main.ts
import type { StorybookConfig } from '@storybook/react-vite'; // or your framework
const config: StorybookConfig = {
stories: ['../src/**/*.stories.@(js|jsx|ts|tsx|mdx)'],
addons: [
'@storybook/addon-essentials',
'@lukethacoder/storybook-addon-shiki',
],
framework: '@storybook/react-vite',
};
export default config;
That's it. Start Storybook and every code block — in the Docs tab, the Source panel, your MDX pages — is now powered by Shiki.
Shiki Stylesheet
This extensions does not bring its own CSS. You bring your own, you configure it how you like. If you want a drop in "it just works" CSS file, see the ./.storybook/styles.css file.
Configuration
Themes
By default the addon uses vitesse-dark. Pass a shiki key when registering the addon to customise:
// .storybook/main.ts
import type { StorybookConfig } from '@storybook/react-vite';
const config: StorybookConfig = {
addons: [
'@storybook/addon-essentials',
{
name: '@lukethacoder/storybook-addon-shiki',
options: {
shiki: {
// any valid shiki theme - `import { type BundledLanguage } from 'shiki'`
theme: 'one-dark-pro',
// BYO list of languages that you wish to use
langs: [
'jsx',
'tsx',
'typescript',
'javascript',
'css',
'html',
'bash',
'json',
'yaml',
'markdown',
'graphql',
'svelte',
],
// requires installing `@shikijs/transformers`
transformers: {
// Enable notation-based features (use comments like [!code highlight])
notationHighlight: true,
notationDiff: true,
notationFocus: true,
notationErrorLevel: true,
notationWordHighlight: true,
// Enable meta-based features (use meta strings like {1-3})
focus: true,
highlight: true,
// Enable diff indicators
diff: true,
// Enable colorized brackets - requires installing `@shikijs/colorized-brackets`
colorizedBrackets: true,
},
},
},
},
],
};
export default config;
Browse all available themes at shiki.style/themes.
Pre-loading languages
Shiki loads language grammars on-demand. To ensure specific languages are available immediately and avoid any first-render delay, declare them upfront:
{
name: '@lukethacoder/storybook-addon-shiki',
options: {
shiki: {
theme: 'one-dark-pro',
langs: ['tsx', 'typescript', 'bash', 'json', 'yaml', 'rust', 'python'],
},
},
}
The following languages are pre-loaded by default: jsx, tsx, typescript, javascript, css, html, bash, json, markdown.
All options
/**
* Options for configuring the Shiki addon.
* These can be set in .storybook/main.ts when registering the addon,
* or overridden per-story via parameters.shiki.
*/
export interface ShikiAddonOptions {
/**
* The Shiki theme to use for syntax highlighting.
* @default 'vitesse-dark'
*/
theme?: BundledTheme | string;
/**
* Languages to pre-load. Shiki loads grammars lazily by default; listing
* the languages your docs use here avoids any first-render flash.
*
* @default Common web languages are loaded automatically.
*/
langs?: (BundledLanguage | string)[];
/**
* Provide your own pre-built Shiki highlighter instance. When supplied the
* addon will use it as-is and skip its own initialisation. Useful if you
* already create a highlighter in your Storybook config.
*/
highlighter?: HighlighterGeneric<BundledLanguage, BundledTheme>;
/**
* Configure which Shiki transformers to enable globally.
* Requires @shikijs/transformers to be installed.
*
* @example
* ```ts
* transformers: {
* focus: true,
* highlight: true,
* diff: true,
* }
* ```
*/
transformers?: TransformerConfig;
}
How it works
This addon uses a pure runtime approach with client-side DOM patching — no fragile module aliasing, no bundler hacks. This makes it incredibly resilient to Storybook internal changes.
Runtime DOM Patching
The addon includes a runtime patcher that runs entirely in the browser:
- Storybook renders normally — Code blocks render using Storybook's default Prism syntax highlighting
- DOM observer activates — A
MutationObserverwatches for PrismJS code blocks as they're added to the DOM - Code extraction — When a Prism block is detected, the addon extracts the raw code text (preserving any transformer notation like
[!code highlight]) - Shiki re-rendering — The code is re-highlighted using Shiki with your configured theme and transformers
- DOM replacement — The original Prism element is hidden (
display: none) and the Shiki version is inserted as a sibling - Content watching — The addon continues to watch the hidden Prism elements for content changes and automatically regenerates Shiki output when needed
- Cleanup — When Prism elements are removed from the DOM, their corresponding Shiki versions are cleaned up automatically
This approach ensures comprehensive coverage:
- ✅ Docs tab "Show code" blocks
- ✅ MDX code blocks (
```jsx) - ✅ Source panel (
<Source />blocks) - ✅ Controls panel (argTypes with control descriptions)
- ✅ Code Panel (in the manager context)
- ✅ Autodocs (auto-generated documentation)
- ✅ Dynamic content stays in sync as stories change
Why Runtime Patching?
Previous approaches relied on bundler-level module aliasing (proxying @storybook/addon-docs/blocks, etc.) which was:
- ❌ Fragile and broke with Storybook internal changes
- ❌ Complex to maintain across different bundler configurations
- ❌ Difficult to debug when issues occurred
Runtime patching is:
- ✅ Resilient — Works regardless of how Storybook's internals change
- ✅ Simple — One clear interception point in the DOM
- ✅ Universal — Works with any bundler (Vite, Webpack, etc.)
- ✅ Debuggable — All logic is in one place and runs in the browser
Configuration Sharing
Addon options configured in main.ts are made available to both preview and manager contexts:
- Preview context: Via a virtual module (
@lukethacoder/storybook-addon-shiki/options) generated by the preset'sviteFinal/webpackFinalhooks - Manager context: Via a global variable (
window.__STORYBOOK_ADDON_SHIKI_OPTIONS__) injected through themanagerHeadhook
This ensures consistent theming and configuration across all code blocks, regardless of where they render.
Building from source / contributing
git clone https://github.com/lukethacoder/storybook-addon-shiki
cd storybook-addon-shiki
# install packages
pnpm install
# compile with tsup
pnpm build
# start the dev Storybook (uses the addon on itself)
pnpm storybook
Publishing NPM Package
Manual package updating
pnpm whoami
pnpm login
pnpm publish --access public # add --tag TAG_NAME if required
Compatibility
| Storybook | Builder | Status |
|---|---|---|
| 8.x | Vite | ❓ |
| 8.x | Webpack 5 | ❓ |
| 9.x / 10.x | Vite | ✅ |
| 9.x / 10.x | Webpack 5 | ✅ |
License
MIT
- lukethacoder