Open-source project
AVGVSTVS96/react-shiki avatar
AVGVSTVS96/react-shiki

react-shiki brings Shiki syntax highlighting to React without raw HTML injection

Syntax highlighting hook and component for React, powered by Shiki

535 stars10 forksTypeScriptMIT

At a glance

What is it?
react-shiki is a React component and hook that highlights code with Shiki on the client, offering React elements or HTML strings as output, three bundle entry points that trade language coverage against bundle size, embedded language detection, custom TextMate themes and streamed code support. It suits a React documentation or blog app, and it is a browser-side highlighter rather than a build-time one.
Who is it for?
Adopt react-shiki if you are building a React app that displays code and want Shiki's TextMate accuracy without a markdown build step, because the library highlights on the client and can return React elements instead of an HTML string, which means no dangerouslySetInnerHTML in your tree. Do not adopt it if your code blocks are static and known at build time, since highlighting them at build time costs nothing at runtime and removes the client bundle entirely.
Can I use it commercially?
Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 6 days ago.
What is it written in?
Mainly TypeScript, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 22, 2026, and from our analysis. They are not legal advice.

Editorial analysis

A client-side highlighter with two ways to call it

react-shiki is described as a performant client-side syntax highlighting component and hook for React, built with Shiki, and the package description puts it the same way. The library gives you both entry points at once: a ShikiHighlighter component and a useShikiHighlighter hook for more flexibility. The component usage takes a language and a theme as props and renders its children as the code.

bash
npm i react-shiki
tsx
import ShikiHighlighter from "react-shiki";

function CodeBlock() {
  return (
    <ShikiHighlighter language="jsx" theme="ayu-dark">
      {code.trim()}
    </ShikiHighlighter>
  );
}

The hook takes the code, the language and a theme, and returns the highlighted result, which is the form to use when you need the markup somewhere other than inside the component's own wrapper.

tsx
import { useShikiHighlighter } from "react-shiki";

function CodeBlock({ code, language }) {
  const highlightedCode = useShikiHighlighter(code, language, "github-dark");

  return <div className="code-block">{highlightedCode}</div>;
}

The library is MIT licensed, the default branch is main, the homepage is react-shiki.vercel.app, and the last push to the repository was on 2026-10-18, with releases 0.11.2 on 2026-10-06, 0.11.1 in August 2026 and 0.11.0 in July 2026. The repository is a pnpm workspace with a package/ directory for the library and a playground/ directory for manual testing, and the root package.json wires the workspace scripts through pnpm filters.

Output as React elements rather than an HTML string

The output decision is the part of the design that matters most for a React codebase. The feature list describes flexible output: you can choose between React elements, with no dangerouslySetInnerHTML, or HTML strings. That distinction has consequences beyond aesthetics. An HTML string has to be injected into the DOM with dangerouslySetInnerHTML, which means your Content Security Policy needs an unsafe-inline style or script exception for the highlighted markup, and any XSS review of your markdown pipeline has to consider that the highlighted output is being injected rather than composed. With React elements, the highlighted spans become part of the virtual DOM, styled by ordinary CSS, and the same concerns about untrusted code text apply to the code content itself rather than to the rendering mechanism. The library also passes through all the options supported by Shiki's codeToHast, which means the underlying Shiki API is available without wrapping, and it supports custom Shiki transformers in addition to those options.

Three bundle entry points and what each one costs

The bundle story is the most detailed part of the README, and it is the decision most projects will have to make. The main react-shiki entry is the full bundle at about 6.4MB minified and 1.2MB gzipped, of which react-shiki itself is about 9KB minified and 4KB gzipped, and it includes all Shiki languages and themes with zero configuration. The web entry point react-shiki/web is about 3.8MB minified and 707KB gzipped and carries web-focused languages, meaning HTML, CSS, JS, TS, JSON, Markdown, Vue, JSX and Svelte, and it is described as a drop-in replacement for the main entry point.

tsx
import ShikiHighlighter from 'react-shiki/web';

The minimal entry point react-shiki/core is about 9KB minified, roughly 4KB gzipped, plus the themes and languages you import yourself, and it requires you to build a highlighter with dynamic imports.

tsx
import ShikiHighlighter, { 
  createHighlighterCore,        // re-exported from shiki/core
  createOnigurumaEngine,        // re-exported from shiki/engine/oniguruma
  createJavaScriptRegexEngine,  // re-exported from shiki/engine/javascript
} from 'react-shiki/core';

// Create custom highlighter with dynamic imports to optimize client-side bundle size
const highlighter = await createHighlighterCore({
  themes: [import('@shikijs/themes/nord')],
  langs: [import('@shikijs/langs/typescript')],
  engine: createOnigurumaEngine(import('shiki/wasm')) 
    // or createJavaScriptRegexEngine()
});

<ShikiHighlighter highlighter={highlighter} language="typescript" theme="nord">
  {code}
</ShikiHighlighter>

Note what is in that last snippet: the library itself is tiny, and the size is in the languages and themes you pull in. That is why Shiki dynamically imports only the languages and themes used on a page, and why the core entry point exists at all.

Choosing a regular expression engine

Shiki offers three engines and the README tabulates them with payload, startup cost and grammar coverage, which is the comparison that decides whether a browser deployment works. Oniguruma is the default, with about 150KB of payload as a 466KB WASM file, a startup cost of fetch plus compile WASM, and it is the reference engine supporting all grammars. The JavaScript RegExp engine is about 20KB with no startup cost since it is plain JavaScript, supports all built-in languages according to the compatibility table, and is strict by default in the full and web bundles. The JavaScript Raw engine is about 1KB with no startup cost but supports pre-compiled grammars only, and the README links a known bug for it. The payload sizes are measured against shiki 4.3.0, with the onig.wasm file at 466KB, 149KB gzipped, and the JavaScript engine's pattern transpiler at 57KB, 20KB gzipped. The practical reading is that Oniguruma is the safe default for language coverage, and the JavaScript engine is the one to pick when you have verified that your languages work under it.

Embedded languages, streaming and code block chrome

Three features target real content rather than toy snippets. Embedded language detection comes from Shiki's guessEmbeddedLanguages, which handles a TypeScript block fenced inside Markdown automatically, so you do not have to hand-label nested fences. Streamed code highlighting is supported, and because streamed code arrives in fragments, the library can throttle the highlighting work rather than re-running it on every token. And the ShikiHighlighter component displays a language label for each code block when showLanguage is set to true, which is the default. Line numbers are optional, with a customizable starting number and styling, and the generated code blocks and language labels are customisable. The library also includes minimal default styles for code blocks, so a first render looks reasonable before you write any CSS. For markdown pipelines there is a documented integration with react-markdown, and a separate section on handling inline code, which is the case where a full highlighter is usually overkill.

Themes, languages and the multi-theme story

Custom TextMate themes and languages are fully supported, passed either as objects or as dynamic imports, and the README documents how to load a theme, how to add a custom language, and how to preload languages. Language aliases are supported too, which matters when your content says js but your theme expects javascript. Multi-theme support is handled through two documented routes. The recommended one uses the light-dark() CSS function, and the alternative is CSS theme switching, and the difference matters because the light-dark approach lets the browser pick the theme without a second render or a state variable. Custom transformers are supported as well, so you can adjust the output that Shiki's codeToHast produces. The combination of themes, languages and transformers arriving through Shiki options means the React wrapper stays thin, and the README's own description of the library as a hook and a component with Shiki underneath reflects that.

Where react-shiki is the wrong choice

Four boundaries are worth naming. The first is that highlighting on the client is a cost. Every page that shows code pays for the grammar and the theme download and the highlight work in the browser, and on a documentation site with many blocks that adds up. The alternative is to highlight at build time, in the markdown pipeline, and ship plain HTML, which is free at runtime and produces the same visual result; the README does not describe a build-time mode, which means choosing react-shiki means choosing the client-side path. The second is the bundle floor. Even the minimal entry point needs the WASM or the JavaScript engine, and the core bundle still asks you to configure a highlighter with dynamic imports, which is real setup for a small site. The third is that the component renders a code block, not a document. It does not parse markdown, it does not handle inline code, and the react-markdown integration is a documented pattern rather than a bundled feature, so if your content pipeline is markdown-first you are assembling two pieces. The fourth is the version churn of a 0.x package with releases every few weeks, where react-shiki 0.11.0, 0.11.1 and 0.11.2 arrived in July, August and October 2026, so pinning a version in a lockfile matters more here than it would for a mature library. The sensible alternative for a site with static content is a build-time highlighter in the markdown pipeline, and the alternative for a genuinely dynamic app that highlights code the user just typed is react-shiki with the core entry point and the JavaScript engine, where the client-side cost is unavoidable and the bundle is under your control.

Editorial conclusion

Adopt react-shiki if you are building a React app that displays code and want Shiki's TextMate accuracy without a markdown build step, because the library highlights on the client and can return React elements instead of an HTML string, which means no dangerouslySetInnerHTML in your tree. Do not adopt it if your code blocks are static and known at build time, since highlighting them at build time costs nothing at runtime and removes the client bundle entirely. Verify two things before you commit to a bundle entry point: which languages and themes your pages actually use, because the full bundle is about 1.2MB gzipped while the minimal core bundle is about 4KB gzipped plus only the languages you import, and whether the JavaScript regular expression engine covers your grammars, because the README's engine table marks the raw engine as pre-compiled grammars only with a known bug. The practical first step is to install react-shiki, render one ShikiHighlighter block with the github-dark theme, and measure the bundle before you decide between the full and web entry points.

Frequently asked questions

What is react-shiki?

It is a client-side syntax highlighting component and hook for React built with Shiki, offering a ShikiHighlighter component and a useShikiHighlighter hook. It can return React elements with no dangerouslySetInnerHTML, or HTML strings. It is MIT licensed and published as react-shiki.

How do I install and use react-shiki?

Run npm i react-shiki, then import ShikiHighlighter from react-shiki and pass a language and a theme prop with the code as children, or import useShikiHighlighter from react-shiki and call it with the code, the language and a theme. The component also accepts a highlighter prop when you build one yourself.

What are the three react-shiki bundle entry points?

The full react-shiki bundle is about 1.2MB gzipped with all Shiki languages and themes, react-shiki/web is about 707KB gzipped with web-focused languages such as HTML, CSS, JS, TS, JSON, Markdown, Vue, JSX and Svelte, and react-shiki/core is about 4KB gzipped plus the languages and themes you import yourself through createHighlighterCore.

Which syntax highlighting engine should I use with react-shiki?

The README tabulates three. Oniguruma is the default at about 150KB gzipped with a fetch plus WASM compile startup and supports all grammars. The JavaScript RegExp engine is about 20KB with no startup cost and supports all built-in languages, and is strict by default in the full and web bundles. The JavaScript Raw engine is about 1KB but supports pre-compiled grammars only and the README links a known bug for it.

Does react-shiki support multiple themes and embedded languages?

Yes. It supports custom TextMate themes and languages passed as objects or dynamic imports, multi-theme support through the light-dark() CSS function or CSS theme switching, and automatic highlighting of embedded languages such as TypeScript fenced inside Markdown via Shiki's guessEmbeddedLanguages. Language aliases and custom transformers are also supported.

Official sources

  1. AVGVSTVS96/react-shiki on GitHub
  2. License: MIT
  3. Project website
  4. README
  5. Releases
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/avgvstvs96-react-shiki.svg)](https://hysenlabs.com/projects/avgvstvs96-react-shiki)

Was this useful?