Skip to content

Commit 4afc211

Browse files
committed
Update README
1 parent 94fa71a commit 4afc211

File tree

1 file changed

+121
-19
lines changed

1 file changed

+121
-19
lines changed

package/README.md

Lines changed: 121 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,18 @@
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+
> [!NOTE]
2+
> This package is still a work in progress, fully functional but not extensively tested`
33
44
# 🎨 [react-shiki](https://react-shiki.vercel.app/)
55

66
Syntax highlighting component and hook for react using [Shiki](https://shiki.matsu.io/)
77

8-
[See the demo page for a version of this README with highlighted code blocks showcasing several Shiki themes!](https://react-shiki.vercel.app/)
8+
[See the demo page with highlighted code blocks showcasing several Shiki themes!](https://react-shiki.vercel.app/)
99

1010
## Features
1111

1212
- 🖼️ Provides a `ShikiHighlighter` component for highlighting code as children, as well as a `useShikiHighlighter` hook for more flexibility
1313
- 🔐 No `dangerouslySetInnerHTML`, output from Shiki is parsed using `html-react-parser`
1414
- 📦 Supports all Shiki languages and themes
15+
- 🚰 Supports performant highlighting of streamed code on the client, with optional throttling
1516
- 📚 Includes minimal default styles for code blocks
1617
- 🚀 Shiki dynamically imports only the languages and themes used on a page, optimizing for performance
1718
- 🖥️ `ShikiHighlighter` component displays a language label for each code block when `showLanguage` is set to `true` (default)
@@ -20,7 +21,7 @@ Syntax highlighting component and hook for react using [Shiki](https://shiki.mat
2021
## Installation
2122

2223
```bash
23-
[pnpm|bun|yarn|npm] [add|install] react-shiki
24+
[pnpm|bun|yarn|npm] install react-shiki
2425
```
2526

2627
## Usage
@@ -74,11 +75,27 @@ function Houston() {
7475
}
7576
```
7677

78+
react-shiki exports `isInlineCode` to check if a code block is inline.
79+
80+
```tsx
81+
const isInline: boolean | undefined = node ? isInlineCode(node) : undefined;
82+
83+
return !isInline ? (
84+
<ShikiHighlighter language={language} theme={"houston"} {...props}>
85+
{String(children)}
86+
</ShikiHighlighter>
87+
) : (
88+
<code className={className} {...props}>
89+
{children}
90+
</code>
91+
);
92+
```
93+
7794
It can also be used with `react-markdown`:
7895

7996
```tsx
8097
import type { ReactNode } from "react";
81-
import ShikiHighlighter, { isInlineCode, type Element } from "react-shiki";
98+
import ShikiHighlighter, { type Element } from "react-shiki";
8299

83100
interface CodeHighlightProps {
84101
className?: string | undefined;
@@ -95,17 +112,9 @@ export const CodeHighlight = ({
95112
const match = className?.match(/language-(\w+)/);
96113
const language = match ? match[1] : undefined;
97114

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-
);
115+
<ShikiHighlighter language={language} theme={"houston"} {...props}>
116+
{String(children)}
117+
</ShikiHighlighter>;
109118
};
110119
```
111120

@@ -124,9 +133,103 @@ import { CodeHighlight } from "./CodeHighlight";
124133
</ReactMarkdown>;
125134
```
126135

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:
136+
### Custom themes
128137

129-
```tsx
138+
```tsx:title=CodeHighlight.tsx
139+
import tokyoNight from '@styles/tokyo-night.mjs';
140+
141+
<ShikiHighlighter language="tsx" theme={tokyoNight}>
142+
{String(code)}
143+
</ShikiHighlighter>;
144+
```
145+
146+
## Client-side highlighting
147+
148+
This works great for highlighting in real-time on the client, I use it for an LLM chatbot UI, it renders markdown and highlights code in memoized chat messages. It also Supports an optional delay between highlights for throttling.
149+
150+
Using `useShikiHighlighter` hook:
151+
152+
```tsx title=CodeHighlight.tsx
153+
import type { ReactNode } from "react";
154+
import { isInlineCode, useShikiHighlighter, type Element } from "react-shiki";
155+
import tokyoNight from "@styles/tokyo-night.mjs";
156+
157+
interface CodeHighlightProps {
158+
className?: string | undefined;
159+
children?: ReactNode | undefined;
160+
node?: Element | undefined;
161+
}
162+
163+
export const CodeHighlight = ({
164+
className,
165+
children,
166+
node,
167+
...props
168+
}: CodeHighlightProps) => {
169+
const code = String(children);
170+
const language = className?.match(/language-(\w+)/)?.[1];
171+
172+
const isInline = node ? isInlineCode(node) : false;
173+
174+
const highlightedCode = useShikiHighlighter(language, code, tokyoNight, {
175+
delay: 150,
176+
});
177+
178+
return !isInline ? (
179+
<div className="shiki not-prose relative [&_pre]:overflow-auto [&_pre]:rounded-lg [&_pre]:px-6 [&_pre]:py-5">
180+
{language ? (
181+
<span className="absolute right-3 top-2 text-xs tracking-tighter text-muted-foreground/85">
182+
{language}
183+
</span>
184+
) : null}
185+
{highlightedCode}
186+
</div>
187+
) : (
188+
<code className={className} {...props}>
189+
{children}
190+
</code>
191+
);
192+
};
193+
```
194+
195+
Or using the `ShikiHighlighter` component:
196+
197+
```tsx title=CodeHighlight.tsx
198+
import type { ReactNode } from "react";
199+
import ShikiHighlighter, { isInlineCode, type Element } from "react-shiki";
200+
201+
interface CodeHighlightProps {
202+
className?: string | undefined;
203+
children?: ReactNode | undefined;
204+
node?: Element | undefined;
205+
}
206+
207+
export const CodeHighlight = ({
208+
className,
209+
children,
210+
node,
211+
...props
212+
}: CodeHighlightProps): JSX.Element => {
213+
const match = className?.match(/language-(\w+)/);
214+
const language = match ? match[1] : undefined;
215+
216+
const isInline: boolean | undefined = node ? isInlineCode(node) : undefined;
217+
218+
return !isInline ? (
219+
<ShikiHighlighter language={language} theme={"houston"} {...props}>
220+
{String(children)}
221+
</ShikiHighlighter>
222+
) : (
223+
<code className={className} {...props}>
224+
{children}
225+
</code>
226+
);
227+
};
228+
```
229+
230+
Passed to `react-markdown` as a code component in memoized chat messages:
231+
232+
```tsx title=ChatMessages.tsx
130233
const RenderedMessage = React.memo(({ message }: { message: Message }) => (
131234
<div className={cn(messageStyles[message.role])}>
132235
<ReactMarkdown components={{ code: CodeHighlight }}>
@@ -145,4 +248,3 @@ export const ChatMessages = ({ messages }: { messages: Message[] }) => {
145248
);
146249
};
147250
```
148-

0 commit comments

Comments
 (0)