react-shiki 0.2.0 → 0.2.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/README.md +198 -37
  2. package/package.json +1 -1
package/README.md CHANGED
@@ -1,41 +1,68 @@
1
- > [!WARNING]
2
- > This package is still a work in progress, it is not yet recommended for production use. Contributions are welcome! My goal is to eventually build this out as a near-drop-in replacement for `react-syntax-highlighter`
1
+ # 🎨 [react-shiki](https://npmjs.com/react-shiki)
3
2
 
4
- # 🎨 [react-shiki](https://react-shiki.vercel.app/)
3
+ > [!NOTE]
4
+ > This package is still a work in progress, fully functional but not
5
+ > extensively tested.
5
6
 
6
- Syntax highlighting component and hook for react using [Shiki](https://shiki.matsu.io/)
7
+ Performant server and client side syntax highlighting component + hook
8
+ for react using [Shiki](https://shiki.matsu.io/)
7
9
 
8
- [See the demo page for a version of this README with highlighted code blocks showcasing several Shiki themes!](https://react-shiki.vercel.app/)
10
+ [See the demo page with highlighted code blocks showcasing several Shiki themes!](https://react-shiki.vercel.app/)
11
+
12
+ <!--toc:start-->
13
+
14
+ - 🎨 [react-shiki](https://npmjs.com/react-shiki)
15
+ - [Features](#features)
16
+ - [Installation](#installation)
17
+ - [Usage](#usage)
18
+ - [`react-markdown`](#react-markdown)
19
+ - [Custom themes](#custom-themes)
20
+ - [Client-side highlighting](#client-side-highlighting)
21
+ - [Throttling real-time highlighting](#throttling-real-time-highlighting)
22
+ - [Streaming and LLM chat UI](#streaming-and-llm-chat-ui)
23
+ <!--toc:end-->
9
24
 
10
25
  ## Features
11
26
 
12
- - 🖼️ Provides a `ShikiHighlighter` component for highlighting code as children, as well as a `useShikiHighlighter` hook for more flexibility
27
+ - 🖼️ Provides a `ShikiHighlighter` component for highlighting code as children,
28
+ as well as a `useShikiHighlighter` hook for more flexibility
13
29
  - 🔐 No `dangerouslySetInnerHTML`, output from Shiki is parsed using `html-react-parser`
14
- - 📦 Supports all Shiki languages and themes
30
+ - 📦 Supports all Shiki languages and themes in addition to
31
+ - 🖌️ Full support for custom TextMate themes in a JavaScript object format
32
+ - 🚰 Performant highlighting of streamed code on the client, with optional throttling
15
33
  - 📚 Includes minimal default styles for code blocks
16
- - 🚀 Shiki dynamically imports only the languages and themes used on a page, optimizing for performance
17
- - 🖥️ `ShikiHighlighter` component displays a language label for each code block when `showLanguage` is set to `true` (default)
18
- - 🎨 Users can customize the styling of the generated code blocks by passing a `style` object or a `className`
34
+ - 🚀 Shiki dynamically imports only the languages and themes used on a page,
35
+ optimizing for performance
36
+ - 🖥️ `ShikiHighlighter` component displays a language label for each code block
37
+ when `showLanguage` is set to `true` (default)
38
+ - 🎨 Users can customize the styling of the generated code blocks by passing
39
+ a `style` object or a `className`
19
40
 
20
41
  ## Installation
21
42
 
22
43
  ```bash
23
- [pnpm|bun|yarn|npm] [add|install] react-shiki
44
+ [pnpm|bun|yarn|npm] install react-shiki
24
45
  ```
25
46
 
26
47
  ## Usage
27
48
 
28
- You can use the `ShikiHighlighter` component, or the `useShikiHighlighter` hook to highlight code.
49
+ You can use the `ShikiHighlighter` component, or the `useShikiHighlighter` hook
50
+ to highlight code.
29
51
 
30
- `useShikiHighlighter` is a hook that takes in the code to be highlighted, the language, and the theme, and returns the highlighted code as React elements. It's useful for users who want full control over the rendering of highlighted code.
52
+ `useShikiHighlighter` is a hook that takes in the code to be highlighted, the
53
+ language, and the theme, and returns the highlighted code as React elements.
54
+ It's useful for users who want full control over the rendering of highlighted
55
+ code.
31
56
 
32
57
  ```tsx
33
- const highlightedCode = useShikiHighlighter(code, language, theme);
58
+ const highlightedCode = useShikiHighlighter(code, language, theme, options);
34
59
  ```
35
60
 
36
- The `ShikiHighlighter` component is imported in your project, with the code to be highlighted passed as it's children.
61
+ The `ShikiHighlighter` component is imported in your project, with the code to
62
+ be highlighted passed as it's children.
37
63
 
38
- Shiki automatically handles dynamically importing only the languages and themes used on the page.
64
+ Shiki automatically handles dynamically importing only the languages and themes
65
+ used on the page.
39
66
 
40
67
  ```tsx
41
68
  function CodeBlock() {
@@ -49,11 +76,15 @@ function CodeBlock() {
49
76
 
50
77
  The component accepts several props in addition to language and theme:
51
78
 
52
- - `showLanguage: boolean` - Shows the language name in the top right corner of the code block.
53
- - `addDefaultStyles`: boolean - Adds default styles (padding, overflow handling, and border-radius) to the code block.
54
- - `as: string` - The component to be rendered. Defaults to 'pre'.
55
- - `className: string` - Class name to be passed to the component.
56
- - `style: object` - Style object to be passed to the component.
79
+ - `showLanguage: boolean` - Shows the language name in the top right corner of
80
+ the code block
81
+ - `addDefaultStyles`: boolean - Adds default styles (padding, overflow handling,
82
+ and border-radius) to the code block
83
+ - `as: string` - The component to be rendered. Defaults to 'pre'
84
+ - `delay: number` - Delay between highlights in milliseconds, useful for throttling
85
+ rapid highlighting on the client
86
+ - `className: string` - Class name to be passed to the component
87
+ - `style: object` - Style object to be passed to the component
57
88
 
58
89
  ```tsx
59
90
  function Houston() {
@@ -74,11 +105,27 @@ function Houston() {
74
105
  }
75
106
  ```
76
107
 
77
- It can also be used with `react-markdown`:
108
+ **react-shiki** exports `isInlineCode` to check if a code block is inline:
109
+
110
+ ```tsx
111
+ const isInline: boolean | undefined = node ? isInlineCode(node) : undefined;
112
+
113
+ return !isInline ? (
114
+ <ShikiHighlighter language={language} theme={"houston"} {...props}>
115
+ {String(children)}
116
+ </ShikiHighlighter>
117
+ ) : (
118
+ <code className={className} {...props}>
119
+ {children}
120
+ </code>
121
+ );
122
+ ```
123
+
124
+ ### `react-markdown`
78
125
 
79
126
  ```tsx
80
127
  import type { ReactNode } from "react";
81
- import ShikiHighlighter, { isInlineCode, type Element } from "react-shiki";
128
+ import ShikiHighlighter, { type Element } from "react-shiki";
82
129
 
83
130
  interface CodeHighlightProps {
84
131
  className?: string | undefined;
@@ -95,21 +142,13 @@ export const CodeHighlight = ({
95
142
  const match = className?.match(/language-(\w+)/);
96
143
  const language = match ? match[1] : undefined;
97
144
 
98
- const isInline: boolean | undefined = node ? isInlineCode(node) : undefined;
99
-
100
- return !isInline ? (
101
- <ShikiHighlighter language={language} theme={"houston"} {...props}>
102
- {String(children)}
103
- </ShikiHighlighter>
104
- ) : (
105
- <code className={className} {...props}>
106
- {children}
107
- </code>
108
- );
145
+ <ShikiHighlighter language={language} theme={"houston"} {...props}>
146
+ {String(children)}
147
+ </ShikiHighlighter>;
109
148
  };
110
149
  ```
111
150
 
112
- Pass CodeHighlight to `react-markdown` as a code component:
151
+ Pass `CodeHighlight` to `react-markdown` as a code component:
113
152
 
114
153
  ```tsx
115
154
  import ReactMarkdown from "react-markdown";
@@ -124,9 +163,132 @@ import { CodeHighlight } from "./CodeHighlight";
124
163
  </ReactMarkdown>;
125
164
  ```
126
165
 
127
- This works great for highlighting in realtime on the client, I use it for an LLM chatbot UI, it renders markdown and highlights code in memoized chat messages:
166
+ ### Custom themes
167
+
168
+ ```tsx:title=CodeHighlight.tsx
169
+ import tokyoNight from '@styles/tokyo-night.mjs';
170
+
171
+ <ShikiHighlighter language="tsx" theme={tokyoNight}>
172
+ {String(code)}
173
+ </ShikiHighlighter>;
174
+ ```
175
+
176
+ ## Client-side highlighting
177
+
178
+ react-shiki supports performance-optimized highlighting on the client.
179
+
180
+ ### Throttling real-time highlighting
181
+
182
+ Throttling real-time highlighting on the client is possible with the
183
+ `delay` option.
128
184
 
129
185
  ```tsx
186
+ const highlightedCode = useShikiHighlighter(code, language, theme, {
187
+ delay: 150,
188
+ });
189
+ ```
190
+
191
+ ### Streaming and LLM chat UI
192
+
193
+ react-shiki can be used to highlight streamed code from LLM responses in real-time.
194
+
195
+ I use it for an
196
+ LLM chatbot UI, it renders markdown and highlights code in memoized chat messages.
197
+
198
+ Using `useShikiHighlighter` hook:
199
+
200
+ ```tsx title=CodeHighlight.tsx
201
+ import type { ReactNode } from "react";
202
+ import { isInlineCode, useShikiHighlighter, type Element } from "react-shiki";
203
+ import tokyoNight from "@styles/tokyo-night.mjs";
204
+
205
+ interface CodeHighlightProps {
206
+ className?: string | undefined;
207
+ children?: ReactNode | undefined;
208
+ node?: Element | undefined;
209
+ }
210
+
211
+ export const CodeHighlight = ({
212
+ className,
213
+ children,
214
+ node,
215
+ ...props
216
+ }: CodeHighlightProps) => {
217
+ const code = String(children);
218
+ const language = className?.match(/language-(\w+)/)?.[1];
219
+
220
+ const isInline = node ? isInlineCode(node) : false;
221
+
222
+ const highlightedCode = useShikiHighlighter(language, code, tokyoNight, {
223
+ delay: 150,
224
+ });
225
+
226
+ return !isInline ? (
227
+ <div
228
+ className="shiki not-prose relative [&_pre]:overflow-auto
229
+ [&_pre]:rounded-lg [&_pre]:px-6 [&_pre]:py-5"
230
+ >
231
+ {language ? (
232
+ <span
233
+ className="absolute right-3 top-2 text-xs tracking-tighter
234
+ text-muted-foreground/85"
235
+ >
236
+ {language}
237
+ </span>
238
+ ) : null}
239
+ {highlightedCode}
240
+ </div>
241
+ ) : (
242
+ <code className={className} {...props}>
243
+ {children}
244
+ </code>
245
+ );
246
+ };
247
+ ```
248
+
249
+ Or using the `ShikiHighlighter` component:
250
+
251
+ ```tsx title=CodeHighlight.tsx
252
+ import type { ReactNode } from "react";
253
+ import ShikiHighlighter, { isInlineCode, type Element } from "react-shiki";
254
+
255
+ interface CodeHighlightProps {
256
+ className?: string | undefined;
257
+ children?: ReactNode | undefined;
258
+ node?: Element | undefined;
259
+ }
260
+
261
+ export const CodeHighlight = ({
262
+ className,
263
+ children,
264
+ node,
265
+ ...props
266
+ }: CodeHighlightProps): JSX.Element => {
267
+ const match = className?.match(/language-(\w+)/);
268
+ const language = match ? match[1] : undefined;
269
+
270
+ const isInline: boolean | undefined = node ? isInlineCode(node) : undefined;
271
+
272
+ return !isInline ? (
273
+ <ShikiHighlighter
274
+ language={language}
275
+ theme={"houston"}
276
+ delay={150}
277
+ {...props}
278
+ >
279
+ {String(children)}
280
+ </ShikiHighlighter>
281
+ ) : (
282
+ <code className={className} {...props}>
283
+ {children}
284
+ </code>
285
+ );
286
+ };
287
+ ```
288
+
289
+ Passed to `react-markdown` as a `code` component in memo-ized chat messages:
290
+
291
+ ```tsx title=ChatMessages.tsx
130
292
  const RenderedMessage = React.memo(({ message }: { message: Message }) => (
131
293
  <div className={cn(messageStyles[message.role])}>
132
294
  <ReactMarkdown components={{ code: CodeHighlight }}>
@@ -145,4 +307,3 @@ export const ChatMessages = ({ messages }: { messages: Message[] }) => {
145
307
  );
146
308
  };
147
309
  ```
148
-
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "react-shiki",
3
3
  "description": "Syntax highlighter component for react using shiki",
4
- "version": "0.2.0",
4
+ "version": "0.2.2",
5
5
  "license": "MIT",
6
6
  "author": {
7
7
  "name": "Bassim Shahidy",