React Shiki Syntax Highlighter
react-shiki is a performant client-side syntax highlighting component and hook for React, leveraging the Shiki library. As of version 0.9.3, it offers flexible output options, including React elements to avoid `dangerouslySetInnerHTML` or raw HTML strings for improved performance. The package provides multiple bundle choices (Full, Web, Core) to optimize bundle size depending on the languages and themes required. It fully supports custom TextMate themes, languages, and Shiki transformers. Key features include dynamic language and theme imports for optimal performance, optional line numbers, and performance optimizations like throttling for real-time highlighting. The project maintains a regular patch release cadence, with minor versions introducing features such as output format selection and regex engine customization, ensuring active development and feature expansion.
Common errors
-
Error: [shiki] Language 'mylang' does not exist. Did you forget to load it?
cause The specified language was not loaded by Shiki or has an incorrect identifier.fixEnsure the `language` prop matches a supported Shiki language ID. If it's a custom language, load it using `loadLanguage` or provide it via the `languages` prop. -
TS2307: Cannot find module 'react-shiki/web' or its corresponding type declarations.
cause TypeScript or your bundler cannot locate the specific bundle entry point or its type definitions.fixVerify the import path is correct (`react-shiki/web` or `react-shiki/core`). Ensure your `tsconfig.json` or bundler configuration is set up to resolve module paths correctly for sub-path exports. Restarting your IDE might also help. -
Code block is rendered without highlighting/styling, appears as plain text.
cause The CSS styles provided by `react-shiki` or your custom styles are not being applied, or the component isn't receiving the code/language props correctly.fixEnsure you have imported the minimal default styles (if desired) and that your component props (`code`, `language`, `theme`) are correctly passed. Check browser developer tools for CSS conflicts or missing styles. Review the `warnings` section for potential CSS class name changes.
Warnings
- breaking In version 0.9.3, CSS classnames and line-number CSS variables were renamed to use an `rs-` prefix to improve CSS specificity. While legacy aliases are temporarily kept, they will be removed in a future major release, potentially breaking custom styling.
- gotcha The default `react-shiki` import uses the 'Full Bundle' which is approximately 1.2MB gzipped, including all Shiki languages and themes. This can lead to a large client-side bundle size and slower initial page loads, especially if only a few languages/themes are needed.
- gotcha Shiki dynamically imports languages and themes on demand. While this optimizes initial bundle size by fetching assets as needed, it requires network access to load these resources during runtime. In offline environments or strict Content Security Policy (CSP) setups, this can prevent highlighting.
- gotcha The `outputFormat` prop (introduced in v0.8.0) defaults to `jsx`, which renders React elements. While safer against XSS, rendering raw HTML strings via `outputFormat="html"` can offer significantly better performance for very large code blocks, but requires `dangerouslySetInnerHTML` internally.
Install
-
npm install react-shiki -
yarn add react-shiki -
pnpm add react-shiki
Imports
- ShikiHighlighter
const ShikiHighlighter = require('react-shiki');import ShikiHighlighter from 'react-shiki';
- useShikiHighlighter
import useShikiHighlighter from 'react-shiki';
import { useShikiHighlighter } from 'react-shiki'; - Web Bundle Component
import ShikiHighlighter from 'react-shiki';
import ShikiHighlighter from 'react-shiki/web';
- ShikiHighlighterProps
import type { ShikiHighlighterProps } from 'react-shiki';
Quickstart
import ShikiHighlighter from "react-shiki";
import { useState } from 'react';
function CodeExample() {
const [code, setCode] = useState(`function greet(name: string): string {
return \`Hello, ${name}!\`;
}
const message = greet("World");
console.log(message);
`);
return (
<div>
<label htmlFor="code-input">Edit code:</label>
<textarea
id="code-input"
value={code}
onChange={(e) => setCode(e.target.value)}
rows={10}
cols={50}
style={{ width: '100%', minHeight: '150px' }}
/>
<h2>Highlighted Code:</h2>
<ShikiHighlighter
language="typescript"
theme="ayu-dark"
showLanguage={true}
wrap={true}
>
{code.trim()}
</ShikiHighlighter>
</div>
);
}