11# 🎨 [ react-shiki] ( https://npmjs.com/react-shiki )
22
3- > [ !NOTE]
4- > This library is still in development. More features will be implemented, and the API may change.
5- > Contributions are welcome!
63
74A performant client-side syntax highlighting component and hook for React, built with [ Shiki] ( https://shiki.matsu.io/ ) .
85
96[ See the demo page with highlighted code blocks showcasing several Shiki themes!] ( https://react-shiki.vercel.app/ )
107
118<!-- toc:start-->
12-
139- 🎨 [ react-shiki] ( https://npmjs.com/react-shiki )
1410 - [ Features] ( #features )
1511 - [ Installation] ( #installation )
1612 - [ Usage] ( #usage )
1713 - [ Bundle Options] ( #bundle-options )
14+ - [ ` react-shiki ` (Full Bundle)] ( #react-shiki-full-bundle )
15+ - [ ` react-shiki/web ` (Web Bundle)] ( #react-shikiweb-web-bundle )
16+ - [ ` react-shiki/core ` (Minimal Bundle)] ( #react-shikicore-minimal-bundle )
17+ - [ RegExp Engines] ( #regexp-engines )
1818 - [ Configuration] ( #configuration )
1919 - [ Common Configuration Options] ( #common-configuration-options )
2020 - [ Component-specific Props] ( #component-specific-props )
2121 - [ Multi-theme Support] ( #multi-theme-support )
22+ - [ Making Themes Reactive] ( #making-themes-reactive )
23+ - [ Option 1: Using ` light-dark() ` Function (Recommended)] ( #option-1-using-light-dark-function-recommended )
24+ - [ Option 2: CSS Theme Switching] ( #option-2-css-theme-switching )
2225 - [ Custom Themes] ( #custom-themes )
2326 - [ Custom Languages] ( #custom-languages )
2427 - [ Preloading Custom Languages] ( #preloading-custom-languages )
28+ - [ Language Aliases] ( #language-aliases )
2529 - [ Custom Transformers] ( #custom-transformers )
2630 - [ Line Numbers] ( #line-numbers )
2731 - [ Integration] ( #integration )
2832 - [ Integration with react-markdown] ( #integration-with-react-markdown )
2933 - [ Handling Inline Code] ( #handling-inline-code )
3034 - [ Performance] ( #performance )
3135 - [ Throttling Real-time Highlighting] ( #throttling-real-time-highlighting )
36+ - [ Output Format Optimization] ( #output-format-optimization )
3237 - [ Streaming and LLM Chat UI] ( #streaming-and-llm-chat-ui )
33- <!-- toc:end-->
38+ <!-- toc:end-->
3439
3540## Features
3641
3742- 🖼️ Provides both a ` ShikiHighlighter ` component and a ` useShikiHighlighter ` hook for more flexibility
38- - 🔐 Shiki output is processed from HAST directly into React elements, no ` dangerouslySetInnerHTML ` required
43+ - 🔐 Flexible output: Choose between React elements ( no ` dangerouslySetInnerHTML ` ) or HTML strings for better performance
3944- 📦 Multiple bundle options: Full bundle (~ 1.2MB gz), web bundle (~ 695KB gz), or minimal core bundle for fine-grained bundle control
4045- 🖌️ Full support for custom TextMate themes and languages
4146- 🔧 Supports passing custom Shiki transformers to the highlighter, in addition to all other options supported by ` codeToHast `
@@ -90,7 +95,7 @@ function CodeBlock({ code, language }) {
9095``` tsx
9196import ShikiHighlighter from ' react-shiki' ;
9297```
93- - ** Size** : ~ 6.4MB minified, 1.2MB gzipped
98+ - ** Size** : ~ 6.4MB minified, ~ 1.2MB gzipped (includes ~ 12KB react-shiki)
9499- ** Languages** : All Shiki languages and themes
95100- ** Use case** : Unknown language requirements, maximum language support
96101- ** Setup** : Zero configuration required
@@ -99,7 +104,7 @@ import ShikiHighlighter from 'react-shiki';
99104``` tsx
100105import ShikiHighlighter from ' react-shiki/web' ;
101106```
102- - ** Size** : ~ 3.8MB minified, 695KB gzipped
107+ - ** Size** : ~ 3.8MB minified, ~ 707KB gzipped (includes ~ 12KB react-shiki)
103108- ** Languages** : Web-focused languages (HTML, CSS, JS, TS, JSON, Markdown, Vue, JSX, Svelte)
104109- ** Use case** : Web applications with balanced size/functionality
105110- ** Setup** : Drop-in replacement for main entry point
@@ -124,7 +129,7 @@ const highlighter = await createHighlighterCore({
124129 { code }
125130</ShikiHighlighter >
126131```
127- - ** Size** : Minimal (only what you import)
132+ - ** Size** : ~ 12KB + your imported themes/languages
128133- ** Languages** : User-defined via custom highlighter
129134- ** Use case** : Production apps requiring maximum bundle control
130135- ** Setup** : Requires custom highlighter configuration
@@ -163,6 +168,7 @@ See [Shiki - RegExp Engines](https://shiki.style/guide/regex-engines) for more i
163168| ` transformers ` | ` array ` | ` [] ` | Custom Shiki transformers for modifying the highlighting output |
164169| ` cssVariablePrefix ` | ` string ` | ` '--shiki' ` | Prefix for CSS variables storing theme colors |
165170| ` defaultColor ` | ` string \| false ` | ` 'light' ` | Default theme mode when using multiple themes, can also disable default theme |
171+ | ` outputFormat ` | ` string ` | ` 'react' ` | Output format: 'react' for React nodes, 'html' for HTML string |
166172| ` tabindex ` | ` number ` | ` 0 ` | Tab index for the code block |
167173| ` decorations ` | ` array ` | ` [] ` | Custom decorations to wrap the highlighted tokens with |
168174| ` structure ` | ` string ` | ` classic ` | The structure of the generated HAST and HTML - ` classic ` or ` inline ` |
@@ -253,12 +259,12 @@ Ensure your site sets the `color-scheme` CSS property:
253259 color-scheme : light dark ;
254260}
255261
256- /* Or dynamically with a class */
257- * {
262+ /* Or dynamically for class based dark mode */
263+ :root {
258264 color-scheme : light ;
259265}
260266
261- .dark {
267+ :root .dark {
262268 color-scheme : dark ;
263269}
264270```
@@ -564,121 +570,36 @@ const highlightedCode = useShikiHighlighter(code, "tsx", "github-dark", {
564570});
565571```
566572
567- ### Streaming and LLM Chat UI
568-
569- ` react-shiki ` can be used to highlight streamed code from LLM responses in real-time.
573+ ### Output Format Optimization
570574
571- I use it for an LLM chatbot UI, it renders markdown and highlights
572- code in memoized chat messages.
573-
574- Using ` useShikiHighlighter ` hook:
575+ ` react-shiki ` provides two output formats to balance safety and performance:
575576
577+ ** React Nodes (Default)** - Safer, no ` dangerouslySetInnerHTML ` required
576578``` tsx
577- import type { ReactNode } from " react" ;
578- import { isInlineCode , useShikiHighlighter , type Element } from " react-shiki" ;
579- import tokyoNight from " @styles/tokyo-night.mjs" ;
580-
581- interface CodeHighlightProps {
582- className? : string | undefined ;
583- children? : ReactNode | undefined ;
584- node? : Element | undefined ;
585- }
586-
587- export const CodeHighlight = ({
588- className ,
589- children ,
590- node ,
591- ... props
592- }: CodeHighlightProps ) => {
593- const code = String (children ).trim ();
594- const language = className ?.match (/ language-(\w + )/ )?.[1 ];
595-
596- const isInline = node ? isInlineCode (node ) : false ;
597-
598- const highlightedCode = useShikiHighlighter (code , language , tokyoNight , {
599- delay: 150 ,
600- });
579+ // Hook
580+ const highlightedCode = useShikiHighlighter (code , " tsx" , " github-dark" );
601581
602- return ! isInline ? (
603- <div
604- className = " shiki not-prose relative [&_pre]:overflow-auto
605- [&_pre]:rounded-lg [&_pre]:px-6 [&_pre]:py-5"
606- >
607- { language ? (
608- <span
609- className = " absolute right-3 top-2 text-xs tracking-tighter
610- text-muted-foreground/85"
611- >
612- { language }
613- </span >
614- ) : null }
615- { highlightedCode }
616- </div >
617- ) : (
618- <code className = { className } { ... props } >
619- { children }
620- </code >
621- );
622- };
582+ // Component
583+ <ShikiHighlighter language = " tsx" theme = " github-dark" >
584+ { code }
585+ </ShikiHighlighter >
623586```
624587
625- Or using the ` ShikiHighlighter ` component:
626-
588+ ** HTML String** - 15-45% faster performance
627589``` tsx
628- import type { ReactNode } from " react" ;
629- import ShikiHighlighter , { isInlineCode , type Element } from " react-shiki" ;
630-
631- interface CodeHighlightProps {
632- className? : string | undefined ;
633- children? : ReactNode | undefined ;
634- node? : Element | undefined ;
635- }
636-
637- export const CodeHighlight = ({
638- className ,
639- children ,
640- node ,
641- ... props
642- }: CodeHighlightProps ): JSX .Element => {
643- const match = className ?.match (/ language-(\w + )/ );
644- const language = match ? match [1 ] : undefined ;
645- const code = String (children ).trim ();
646-
647- const isInline: boolean | undefined = node ? isInlineCode (node ) : undefined ;
590+ // Hook (returns HTML string, use dangerouslySetInnerHTML to render)
591+ const highlightedCode = useShikiHighlighter (code , " tsx" , " github-dark" , {
592+ outputFormat: ' html'
593+ });
648594
649- return ! isInline ? (
650- <ShikiHighlighter
651- language = { language }
652- theme = " github-dark"
653- delay = { 150 }
654- { ... props }
655- >
656- { code }
657- </ShikiHighlighter >
658- ) : (
659- <code className = { className } >{ code } </code >
660- );
661- };
595+ // Component (automatically uses dangerouslySetInnerHTML when outputFormat is 'html')
596+ <ShikiHighlighter language = " tsx" theme = " github-dark" outputFormat = " html" >
597+ { code }
598+ </ShikiHighlighter >
662599```
663600
664- Passed to ` react-markdown ` as a ` code ` component in memoized chat messages:
601+ Choose HTML output when performance is critical and you trust the code source. Use the default React output when handling untrusted content or when security is the primary concern.
665602
666- ``` tsx
667- const RenderedMessage = React .memo (({ message }: { message: Message }) => (
668- <div className = { cn (messageStyles [message .role ])} >
669- <ReactMarkdown components = { { code: CodeHighlight }} >
670- { message .content }
671- </ReactMarkdown >
672- </div >
673- ));
674-
675- export const ChatMessages = ({ messages }: { messages: Message [] }) => {
676- return (
677- <div className = " space-y-4" >
678- { messages .map ((message ) => (
679- <RenderedMessage key = { message .id } message = { message } />
680- ))}
681- </div >
682- );
683- };
684- ```
603+ ---
604+
605+ Made with ❤️ by [ Bassim (AVGVSTVS96)] ( https://github.com/AVGVSTVS96 )
0 commit comments