react-shiki 0.11.0 → 0.11.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.
- package/README.md +228 -111
- package/dist/{component-DdW14SOw.mjs → component-Cf8Topxy.mjs} +118 -78
- package/dist/component-Cf8Topxy.mjs.map +1 -0
- package/dist/component-DRPT3D6t.d.mts +1200 -0
- package/dist/core.d.mts +4 -5
- package/dist/core.mjs +2 -2
- package/dist/core.mjs.map +1 -1
- package/dist/factory-DySLHBEo.mjs +17 -0
- package/dist/factory-DySLHBEo.mjs.map +1 -0
- package/dist/index.d.mts +4 -5
- package/dist/index.mjs +6 -12
- package/dist/index.mjs.map +1 -1
- package/dist/style.css +1 -68
- package/dist/web.d.mts +4 -5
- package/dist/web.mjs +7 -13
- package/dist/web.mjs.map +1 -1
- package/package.json +24 -20
- package/dist/component-B-gutZRK.d.mts +0 -546
- package/dist/component-DdW14SOw.mjs.map +0 -1
- package/src/styles/features.css +0 -54
package/README.md
CHANGED
|
@@ -2,10 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
A performant client-side syntax highlighting component and hook for React, built with [Shiki](https://shiki.matsu.io/).
|
|
4
4
|
|
|
5
|
-
[See the demo page with highlighted code blocks showcasing several Shiki themes!](https://react-shiki.vercel.app/)
|
|
6
|
-
|
|
7
5
|
<!--toc:start-->
|
|
8
|
-
- [🎨 react-shiki](
|
|
6
|
+
- [🎨 react-shiki](#-react-shiki)
|
|
9
7
|
- [Features](#features)
|
|
10
8
|
- [Installation](#installation)
|
|
11
9
|
- [Usage](#usage)
|
|
@@ -14,11 +12,14 @@ A performant client-side syntax highlighting component and hook for React, built
|
|
|
14
12
|
- [`react-shiki/web` (Web Bundle)](#react-shikiweb-web-bundle)
|
|
15
13
|
- [`react-shiki/core` (Minimal Bundle)](#react-shikicore-minimal-bundle)
|
|
16
14
|
- [RegExp Engines](#regexp-engines)
|
|
17
|
-
- [
|
|
18
|
-
- [
|
|
19
|
-
|
|
15
|
+
- [Full and Web Bundles](#full-and-web-bundles)
|
|
16
|
+
- [Custom Highlighters (Core Bundle)](#custom-highlighters-core-bundle)
|
|
17
|
+
- [JavaScript Raw Engine](#javascript-raw-engine)
|
|
18
|
+
- [Output Formats](#output-formats)
|
|
20
19
|
- [Configuration](#configuration)
|
|
21
20
|
- [Common Configuration Options](#common-configuration-options)
|
|
21
|
+
- [react-shiki Options](#react-shiki-options)
|
|
22
|
+
- [Shiki Pass-through Options](#shiki-pass-through-options)
|
|
22
23
|
- [Component-specific Props](#component-specific-props)
|
|
23
24
|
- [Embedded Language Highlighting](#embedded-language-highlighting)
|
|
24
25
|
- [Multi-theme Support](#multi-theme-support)
|
|
@@ -36,7 +37,6 @@ A performant client-side syntax highlighting component and hook for React, built
|
|
|
36
37
|
- [Handling Inline Code](#handling-inline-code)
|
|
37
38
|
- [Performance](#performance)
|
|
38
39
|
- [Throttling Real-time Highlighting](#throttling-real-time-highlighting)
|
|
39
|
-
- [Output Formats](#output-formats)
|
|
40
40
|
<!--toc:end-->
|
|
41
41
|
|
|
42
42
|
## Features
|
|
@@ -44,7 +44,7 @@ A performant client-side syntax highlighting component and hook for React, built
|
|
|
44
44
|
- 🖼️ Provides both a `ShikiHighlighter` component and a `useShikiHighlighter` hook for more flexibility
|
|
45
45
|
- 🔐 Flexible output: Choose between React elements (no `dangerouslySetInnerHTML`) or HTML strings
|
|
46
46
|
- 📦 Multiple bundle options: Full bundle (~1.2MB gz), web bundle (~707KB gz), or minimal core bundle for fine-grained bundle control
|
|
47
|
-
- 🖌️ Full support for custom TextMate themes and languages
|
|
47
|
+
- 🖌️ Full support for custom TextMate themes and languages, passed as objects or dynamic imports
|
|
48
48
|
- 🧬 Automatic highlighting of embedded languages (e.g. TypeScript fenced inside Markdown) via Shiki's `guessEmbeddedLanguages`
|
|
49
49
|
- 🔧 Supports passing custom Shiki transformers to the highlighter, in addition to all other options supported by `codeToHast`
|
|
50
50
|
- 🚰 Performant highlighting of streamed code, with optional throttling
|
|
@@ -98,8 +98,8 @@ function CodeBlock({ code, language }) {
|
|
|
98
98
|
```tsx
|
|
99
99
|
import ShikiHighlighter from 'react-shiki';
|
|
100
100
|
```
|
|
101
|
-
- **Size**: ~6.4MB minified, ~1.2MB gzipped (
|
|
102
|
-
- **Languages**: All Shiki languages and themes
|
|
101
|
+
- **Size**: ~6.4MB minified, ~1.2MB gzipped (react-shiki itself: ~9KB minified, ~4KB gzipped)
|
|
102
|
+
- **Languages**: All Shiki [languages](https://shiki.style/languages) and [themes](https://shiki.style/themes)
|
|
103
103
|
- **Exported engines**: `createJavaScriptRegexEngine`, `createJavaScriptRawEngine`
|
|
104
104
|
- **Use case**: Unknown language requirements, maximum language support
|
|
105
105
|
- **Setup**: Zero configuration required
|
|
@@ -108,8 +108,8 @@ import ShikiHighlighter from 'react-shiki';
|
|
|
108
108
|
```tsx
|
|
109
109
|
import ShikiHighlighter from 'react-shiki/web';
|
|
110
110
|
```
|
|
111
|
-
- **Size**: ~3.8MB minified, ~707KB gzipped (
|
|
112
|
-
- **Languages**: Web-focused languages (HTML, CSS, JS, TS, JSON, Markdown, Vue, JSX, Svelte)
|
|
111
|
+
- **Size**: ~3.8MB minified, ~707KB gzipped (react-shiki itself: ~9KB minified, ~4KB gzipped)
|
|
112
|
+
- **Languages**: [Web-focused languages](https://shiki.style/guide/bundles#shiki-bundle-web) (HTML, CSS, JS, TS, JSON, Markdown, Vue, JSX, Svelte)
|
|
113
113
|
- **Exported engines**: `createJavaScriptRegexEngine`, `createJavaScriptRawEngine`
|
|
114
114
|
- **Use case**: Web applications with balanced size/functionality
|
|
115
115
|
- **Setup**: Drop-in replacement for main entry point
|
|
@@ -134,7 +134,7 @@ const highlighter = await createHighlighterCore({
|
|
|
134
134
|
{code}
|
|
135
135
|
</ShikiHighlighter>
|
|
136
136
|
```
|
|
137
|
-
- **Size**: ~
|
|
137
|
+
- **Size**: ~9KB minified (~4KB gzipped) + your imported themes/languages
|
|
138
138
|
- **Languages**: User-defined via custom highlighter
|
|
139
139
|
- **Use case**: Production apps requiring maximum bundle control
|
|
140
140
|
- **Setup**: Requires custom highlighter configuration
|
|
@@ -143,40 +143,57 @@ const highlighter = await createHighlighterCore({
|
|
|
143
143
|
### RegExp Engines
|
|
144
144
|
|
|
145
145
|
Shiki offers three built-in engines for syntax highlighting:
|
|
146
|
-
- **Oniguruma** - Default engine using compiled WebAssembly, offers maximum language support
|
|
147
|
-
- **JavaScript RegExp** - Smaller bundle, faster startup, compiles patterns on-the-fly, recommended for client-side highlighting
|
|
148
|
-
- **JavaScript Raw** - For [pre-compiled languages](https://shiki.style/guide/regex-engines#pre-compiled-languages), skips transpilation step for best performance
|
|
149
146
|
|
|
150
|
-
|
|
147
|
+
| Engine | Payload (gzip) | Startup | Grammar support |
|
|
148
|
+
| --- | --- | --- | --- |
|
|
149
|
+
| **Oniguruma** (default) | ~150 KB (466 KB WASM) | fetch + compile WASM | Reference engine, all grammars |
|
|
150
|
+
| **JavaScript RegExp** | ~20 KB | none, plain JS | All built-in languages ([compatibility table](https://shiki.style/references/engine-js-compat)); [strict by default](#full-and-web-bundles) |
|
|
151
|
+
| **JavaScript Raw** | ~1 KB | none | [Pre-compiled grammars](https://shiki.style/guide/regex-engines#pre-compiled-languages) only, has a [known bug](https://github.com/shikijs/shiki/issues/918) |
|
|
151
152
|
|
|
152
|
-
|
|
153
|
+
Payload sizes measured against shiki 4.3.0: `onig.wasm` is 466 KB (149 KB gzipped), the JavaScript engine's pattern transpiler ([oniguruma-to-es](https://github.com/slevithan/oniguruma-to-es)) is 57 KB (20 KB gzipped), and the raw engine skips the transpiler entirely. Beyond the smaller download, the JavaScript engine also starts instantly (no WASM fetch and compile) and [runs patterns as native JavaScript RegExp](https://shiki.style/guide/regex-engines#javascript-regexp-engine), which is faster for some languages.
|
|
153
154
|
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
createJavaScriptRegexEngine,
|
|
158
|
-
createJavaScriptRawEngine
|
|
159
|
-
} from 'react-shiki';
|
|
155
|
+
#### Full and Web Bundles
|
|
156
|
+
|
|
157
|
+
The full and web bundles use Oniguruma by default. You can swap to the JavaScript engine by passing `'javascript'` to `engine`. react-shiki creates and caches the engine internally, no need to import it, no WASM fetch with `'javascript'`, and it's safe to pass inline:
|
|
160
158
|
|
|
161
|
-
|
|
159
|
+
```tsx
|
|
160
|
+
// Hook
|
|
162
161
|
const highlightedCode = useShikiHighlighter(code, 'typescript', 'github-dark', {
|
|
163
|
-
engine:
|
|
162
|
+
engine: 'javascript'
|
|
164
163
|
});
|
|
165
164
|
|
|
166
|
-
// Component
|
|
167
|
-
|
|
168
|
-
<ShikiHighlighter
|
|
169
|
-
language="typescript"
|
|
170
|
-
theme="github-dark"
|
|
171
|
-
engine={createJavaScriptRawEngine()}
|
|
172
|
-
>
|
|
165
|
+
// Component
|
|
166
|
+
<ShikiHighlighter language="typescript" theme="github-dark" engine="javascript">
|
|
173
167
|
{code}
|
|
174
168
|
</ShikiHighlighter>
|
|
175
169
|
```
|
|
176
170
|
|
|
177
|
-
|
|
171
|
+
> [!NOTE]
|
|
172
|
+
> The underlying highlighter is a singleton created on the first highlight, so pass the same engine at every call site; mixed engines resolve to whichever highlighted first.
|
|
178
173
|
|
|
179
|
-
|
|
174
|
+
To customize engine configuration beyond the named defaults, pass an engine instance. **Create it once at module scope**; this avoids recreating new instances on every render, which would defeat memoization:
|
|
175
|
+
|
|
176
|
+
```tsx
|
|
177
|
+
import { useShikiHighlighter, createJavaScriptRegexEngine } from 'react-shiki';
|
|
178
|
+
|
|
179
|
+
// Ensure this is at the module scope: created once, stable across renders
|
|
180
|
+
const engine = createJavaScriptRegexEngine({ forgiving: true });
|
|
181
|
+
|
|
182
|
+
const highlightedCode = useShikiHighlighter(code, 'typescript', 'github-dark', {
|
|
183
|
+
engine
|
|
184
|
+
});
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
The JavaScript engine is strict by default: it throws when a grammar uses patterns it can't convert. `forgiving: true` skips those patterns for best-effort output. It also accepts `target` and `regexConstructor`, see [Shiki - RegExp Engines](https://shiki.style/guide/regex-engines) for details.
|
|
188
|
+
|
|
189
|
+
The JavaScript Raw engine works the same way, but only with [pre-compiled languages](https://shiki.style/guide/regex-engines#pre-compiled-languages) (`@shikijs/langs-precompiled`). The full and web bundles don't support the raw engine because they bundle standard grammars, you need a [custom highlighter](#custom-highlighters-core-bundle).
|
|
190
|
+
|
|
191
|
+
> [!IMPORTANT]
|
|
192
|
+
> **Heads up:** in a future release, react-shiki's default engine will be swapped from Oniguruma WASM to the JavaScript engine (with [`forgiving`](https://shiki.style/guide/regex-engines#use-with-unsupported-languages) enabled by default).
|
|
193
|
+
|
|
194
|
+
#### Custom Highlighters (Core Bundle)
|
|
195
|
+
|
|
196
|
+
Named engines (`'javascript'`, `'oniguruma'`) don't apply to the core bundle since you create the highlighter with `createHighlighterCore`; instead, use `createJavaScriptRegexEngine` or `createOnigurumaEngine` to create a custom engine instance:
|
|
180
197
|
|
|
181
198
|
```tsx
|
|
182
199
|
import {
|
|
@@ -192,41 +209,130 @@ const highlighter = await createHighlighterCore({
|
|
|
192
209
|
});
|
|
193
210
|
```
|
|
194
211
|
|
|
195
|
-
|
|
212
|
+
##### JavaScript Raw Engine
|
|
196
213
|
|
|
197
|
-
The JavaScript
|
|
214
|
+
The JavaScript Raw engine is the smallest option at only ~1 KB, but requires [pre-compiled languages](https://shiki.style/guide/regex-engines#pre-compiled-languages), and faces a known upstream bug -- [shikijs/shiki:issue#918](https://github.com/shikijs/shiki/issues/918) -- causing some languages (Python, HTML, Perl, and YAML among those reported) to highlight incorrectly.
|
|
198
215
|
|
|
199
216
|
```tsx
|
|
200
|
-
|
|
217
|
+
import {
|
|
218
|
+
useShikiHighlighter,
|
|
219
|
+
createHighlighterCore,
|
|
220
|
+
createJavaScriptRawEngine,
|
|
221
|
+
} from 'react-shiki/core';
|
|
222
|
+
|
|
223
|
+
// Module scope: raw engine + pre-compiled grammars, smallest and fastest
|
|
224
|
+
const highlighter = await createHighlighterCore({
|
|
225
|
+
themes: [import('@shikijs/themes/github-dark')],
|
|
226
|
+
langs: [import('@shikijs/langs-precompiled/typescript')],
|
|
227
|
+
engine: createJavaScriptRawEngine()
|
|
228
|
+
});
|
|
229
|
+
|
|
230
|
+
const highlightedCode = useShikiHighlighter(code, 'typescript', 'github-dark', {
|
|
231
|
+
highlighter
|
|
232
|
+
});
|
|
201
233
|
```
|
|
202
234
|
|
|
203
|
-
|
|
235
|
+
Be sure to verify your language highlights correctly if you choose to use the raw engine ([shikijs/shiki:issue#918](https://github.com/shikijs/shiki/issues/918)).
|
|
236
|
+
|
|
237
|
+
|
|
238
|
+
## Output Formats
|
|
239
|
+
|
|
240
|
+
`react-shiki` can return highlighted code in three formats:
|
|
241
|
+
|
|
242
|
+
**React Nodes (Default)** - Rendered as React elements, no `dangerouslySetInnerHTML` required
|
|
243
|
+
```tsx
|
|
244
|
+
// Hook
|
|
245
|
+
const highlightedCode = useShikiHighlighter(code, "tsx", "github-dark");
|
|
246
|
+
|
|
247
|
+
// Component
|
|
248
|
+
<ShikiHighlighter language="tsx" theme="github-dark">
|
|
249
|
+
{code}
|
|
250
|
+
</ShikiHighlighter>
|
|
251
|
+
```
|
|
252
|
+
|
|
253
|
+
**HTML String** - Rendered via `dangerouslySetInnerHTML`
|
|
254
|
+
```tsx
|
|
255
|
+
// Hook (returns HTML string, use dangerouslySetInnerHTML to render)
|
|
256
|
+
const highlightedCode = useShikiHighlighter(code, "tsx", "github-dark", {
|
|
257
|
+
outputFormat: 'html'
|
|
258
|
+
});
|
|
259
|
+
|
|
260
|
+
// Component (automatically uses dangerouslySetInnerHTML when outputFormat is 'html')
|
|
261
|
+
<ShikiHighlighter language="tsx" theme="github-dark" outputFormat="html">
|
|
262
|
+
{code}
|
|
263
|
+
</ShikiHighlighter>
|
|
264
|
+
```
|
|
265
|
+
|
|
266
|
+
**Shiki Tokens (Experimental)** - Hook-only output (bring your own renderer, react-shiki does not provide one)
|
|
267
|
+
```tsx
|
|
268
|
+
const highlighted = useShikiHighlighter(code, "tsx", "github-dark", {
|
|
269
|
+
outputFormat: "tokens",
|
|
270
|
+
});
|
|
271
|
+
|
|
272
|
+
// Sample only, not a production renderer: you own rendering, memoization,
|
|
273
|
+
// and commit management (critical for streaming highlighting)
|
|
274
|
+
const rendered = highlighted?.tokens.map((line, i) => (
|
|
275
|
+
<div key={i}>
|
|
276
|
+
{line.map((token, j) => (
|
|
277
|
+
<span key={j} style={{ color: token.color }}>
|
|
278
|
+
{token.content}
|
|
279
|
+
</span>
|
|
280
|
+
))}
|
|
281
|
+
</div>
|
|
282
|
+
));
|
|
283
|
+
```
|
|
284
|
+
|
|
285
|
+
The React and HTML formats spend their rendering work in different phases. HTML output skips per-token React element creation and reconciliation, reducing render-phase overhead for large, one-shot highlights, but each update replaces the code block's entire DOM subtree via `innerHTML`. React output pays for element creation and diffing on every update, but commits only incremental DOM mutations, minimizing DOM churn for frequently re-highlighted code such as streaming LLM output.
|
|
286
|
+
|
|
287
|
+
HTML output hands the highlighted markup to the DOM via `dangerouslySetInnerHTML`, so only use it with trusted code sources. The default React output is the safe choice for untrusted content.
|
|
288
|
+
|
|
289
|
+
Token output is for when you need to own rendering yourself; it is intentionally available on the hook, not the `ShikiHighlighter` component, since react-shiki's markup-based features no longer apply once you control the markup. Note that Shiki's `codeToTokens` does not run transformers or decorations, so markup-producing options, including transformers, decorations, line numbers, and `structure`, have no effect on token output.
|
|
204
290
|
|
|
205
291
|
## Configuration
|
|
206
292
|
|
|
207
293
|
### Common Configuration Options
|
|
208
294
|
|
|
209
|
-
|
|
295
|
+
The core inputs, positional arguments on the hook, props/children on the component:
|
|
296
|
+
|
|
297
|
+
| Input | Type | Default | Description |
|
|
210
298
|
| ------------------- | ------------------ | --------------- | ----------------------------------------------------------------------------- |
|
|
211
299
|
| `code` | `string` | - | Code to highlight |
|
|
212
300
|
| `language` | `string \| object` | - | Language to highlight, built-in or custom TextMate grammar object |
|
|
213
301
|
| `theme` | `string \| object` | `'github-dark'` | Single or multi-theme configuration, built-in or custom TextMate theme object |
|
|
214
|
-
|
|
302
|
+
|
|
303
|
+
#### react-shiki Options
|
|
304
|
+
|
|
305
|
+
|
|
306
|
+
| Option | Type | Default | Description |
|
|
307
|
+
| ------------------- | ------------------ | --------------- | ----------------------------------------------------------------------------- |
|
|
308
|
+
| `delay` | `number` | - | Minimum time between highlights (in milliseconds), no throttling by default |
|
|
309
|
+
| `engine` | `'javascript' \| 'oniguruma' \| RegexEngine` | `'oniguruma'` | RegExp engine for syntax highlighting; named engines are created and cached internally |
|
|
310
|
+
| `highlighter` | `Highlighter \| HighlighterCore` | - | Custom highlighter instance, required for the core bundle |
|
|
311
|
+
| `outputFormat` | `string` | `'react'` | Output format: 'react' for React nodes, 'html' for HTML string, or 'tokens' for Shiki tokens (hook only, experimental) |
|
|
312
|
+
| `preloadLanguages` | `array` | `[]` | Preload bundled language IDs, custom grammar objects, or dynamic grammar imports |
|
|
215
313
|
| `customLanguages` | `array` | `[]` | **Deprecated**: use `preloadLanguages` instead. |
|
|
216
|
-
| `preloadLanguages` | `array` | `[]` | Preload bundled language IDs and custom language grammars |
|
|
217
|
-
| `langAlias` | `object` | `{}` | Map of language aliases |
|
|
218
|
-
| `engine` | `RegexEngine` | Oniguruma | RegExp engine for syntax highlighting (Oniguruma, JavaScript RegExp, or JavaScript Raw) |
|
|
219
314
|
| `showLineNumbers` | `boolean` | `false` | Display line numbers alongside code |
|
|
220
315
|
| `startingLineNumber` | `number` | `1` | Starting line number when line numbers are enabled |
|
|
221
316
|
| `highlightLineNumbers` | `number[]` | - | Displayed line numbers to highlight |
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
317
|
+
|
|
318
|
+
#### Shiki Pass-through Options
|
|
319
|
+
|
|
320
|
+
All of Shiki's options pass-through and are accessible in react-shiki.
|
|
321
|
+
|
|
322
|
+
Based on `outputFormat`:
|
|
323
|
+
- `'tokens'`: All options supported by Shiki's [`codeToTokens`](https://github.com/shikijs/shiki/blob/v4.3.0/packages/types/src/tokens.ts#L27)
|
|
324
|
+
- `'html'`: All options supported by Shiki's [`codeToHtml`](https://github.com/shikijs/shiki/blob/v4.3.0/packages/types/src/options.ts#L136)
|
|
325
|
+
- `'react'`: All options supported by Shiki's [`codeToHast`](https://github.com/shikijs/shiki/blob/v4.3.0/packages/types/src/options.ts#L136)
|
|
326
|
+
|
|
327
|
+
Commonly used:
|
|
328
|
+
- `transformers`: modify the highlighting output with Shiki transformers (default: `[]`)
|
|
329
|
+
- `decorations`: wrap ranges of highlighted tokens (default: `[]`)
|
|
330
|
+
- `defaultColor`: default theme mode with multi-theme, `false` to disable (default: `'light'`)
|
|
331
|
+
- `cssVariablePrefix`: prefix for theme color CSS variables (default: `'--shiki-'`)
|
|
332
|
+
- `langAlias`: map custom language names to bundled ones (default: `{}`)
|
|
333
|
+
- `structure`: `'classic'` or `'inline'` HAST/HTML shape (default: `'classic'`)
|
|
334
|
+
- `tabindex`: tab index of the code block (default: `0`)
|
|
335
|
+
- `meta`: arbitrary metadata passed to transformers (default: `{}`)
|
|
230
336
|
|
|
231
337
|
### Component-specific Props
|
|
232
338
|
|
|
@@ -331,7 +437,7 @@ Ensure your site sets the `color-scheme` CSS property:
|
|
|
331
437
|
|
|
332
438
|
For broader browser support or more control, add CSS snippets to your site to enable theme switching with media queries or class-based switching. See [Shiki's documentation](https://shiki.matsu.io/guide/dual-themes) for the required CSS snippets.
|
|
333
439
|
|
|
334
|
-
> **Note**: The `light-dark()` function requires modern browser support. For older browsers, use the manual CSS variables approach.
|
|
440
|
+
> **Note**: The `light-dark()` function requires [modern browser support](https://caniuse.com/wf-light-dark). For older browsers, use the manual CSS variables approach.
|
|
335
441
|
|
|
336
442
|
### Custom Themes
|
|
337
443
|
|
|
@@ -349,6 +455,26 @@ import tokyoNight from "../styles/tokyo-night.json";
|
|
|
349
455
|
const highlightedCode = useShikiHighlighter(code, "tsx", tokyoNight);
|
|
350
456
|
```
|
|
351
457
|
|
|
458
|
+
When using a fine-grained highlighter via `react-shiki/core`: pass the theme as a dynamic import to `createHighlighterCore` so it stays out of the initial chunk, then reference it by its `name` field:
|
|
459
|
+
|
|
460
|
+
```tsx
|
|
461
|
+
const highlighter = await createHighlighterCore({
|
|
462
|
+
themes: [import("../styles/tokyo-night.json")],
|
|
463
|
+
langs: [import("@shikijs/langs/tsx")],
|
|
464
|
+
engine: createJavaScriptRegexEngine(),
|
|
465
|
+
});
|
|
466
|
+
|
|
467
|
+
// Component
|
|
468
|
+
<ShikiHighlighter highlighter={highlighter} language="tsx" theme="tokyo-night">
|
|
469
|
+
{code.trim()}
|
|
470
|
+
</ShikiHighlighter>
|
|
471
|
+
|
|
472
|
+
// Hook
|
|
473
|
+
const highlightedCode = useShikiHighlighter(code, "tsx", tokyoNight, {
|
|
474
|
+
highlighter,
|
|
475
|
+
});
|
|
476
|
+
```
|
|
477
|
+
|
|
352
478
|
### Custom Languages
|
|
353
479
|
|
|
354
480
|
Custom languages should be passed as a TextMate grammar JavaScript object ([example](https://github.com/shikijs/textmate-grammars-themes/blob/main/packages/tm-grammars/grammars/typescript.json)).
|
|
@@ -364,6 +490,28 @@ import mcfunction from "../langs/mcfunction.tmLanguage.json";
|
|
|
364
490
|
const highlightedCode = useShikiHighlighter(code, mcfunction, "github-dark");
|
|
365
491
|
```
|
|
366
492
|
|
|
493
|
+
Passing grammar objects as props works with the full and web bundles, where react-shiki creates the highlighter and loads them for you. To keep custom grammars out of the main bundle, pass them as dynamic imports via `preloadLanguages` and reference them by name — see [Preloading Languages](#preloading-languages).
|
|
494
|
+
|
|
495
|
+
On the core bundle you own the highlighter, so include grammars there as dynamic imports:
|
|
496
|
+
|
|
497
|
+
```tsx
|
|
498
|
+
const highlighter = await createHighlighterCore({
|
|
499
|
+
themes: [import("@shikijs/themes/github-dark")],
|
|
500
|
+
langs: [import("../langs/mcfunction.tmLanguage.json")],
|
|
501
|
+
engine: createJavaScriptRegexEngine(),
|
|
502
|
+
});
|
|
503
|
+
|
|
504
|
+
// Component
|
|
505
|
+
<ShikiHighlighter highlighter={highlighter} language="mcfunction" theme="github-dark">
|
|
506
|
+
{code.trim()}
|
|
507
|
+
</ShikiHighlighter>
|
|
508
|
+
|
|
509
|
+
// Hook
|
|
510
|
+
const highlightedCode = useShikiHighlighter(code, "mcfunction", "github-dark", {
|
|
511
|
+
highlighter,
|
|
512
|
+
});
|
|
513
|
+
```
|
|
514
|
+
|
|
367
515
|
#### Preloading Languages
|
|
368
516
|
|
|
369
517
|
If highlighting is dynamic at runtime, preload custom languages.
|
|
@@ -387,8 +535,29 @@ const highlightedCode = useShikiHighlighter(code, "typescript", "github-dark", {
|
|
|
387
535
|
});
|
|
388
536
|
```
|
|
389
537
|
|
|
538
|
+
Custom grammars can also be dynamic imports, keeping them out of the main bundle. Reference them by the grammar's `name` field:
|
|
539
|
+
|
|
540
|
+
```tsx
|
|
541
|
+
// Module scope: stable identity across renders, fetched on first highlight
|
|
542
|
+
const preloadLanguages = [
|
|
543
|
+
() => import("../langs/mcfunction.tmLanguage.json"),
|
|
544
|
+
() => import("../langs/bosque.tmLanguage.json"),
|
|
545
|
+
];
|
|
546
|
+
|
|
547
|
+
<ShikiHighlighter
|
|
548
|
+
language="mcfunction"
|
|
549
|
+
theme="github-dark"
|
|
550
|
+
preloadLanguages={preloadLanguages}
|
|
551
|
+
>
|
|
552
|
+
{code.trim()}
|
|
553
|
+
</ShikiHighlighter>
|
|
554
|
+
```
|
|
555
|
+
|
|
556
|
+
> [!IMPORTANT]
|
|
557
|
+
> Define dynamic imports at module scope, preferring the getter form (`() => import(...)`). A fresh arrow function on every render re-triggers highlighting; bare promises are compared by type, so swapping one at runtime won't be picked up.
|
|
558
|
+
|
|
390
559
|
> [!NOTE]
|
|
391
|
-
> Bundled languages are loaded on demand and do not need to be preloaded.
|
|
560
|
+
> Bundled languages are loaded on demand and do not need to be preloaded. `preloadLanguages` applies to the full and web bundles; with a custom highlighter, load languages in `createHighlighterCore` instead.
|
|
392
561
|
|
|
393
562
|
### Language Aliases
|
|
394
563
|
|
|
@@ -442,7 +611,7 @@ Line numbers are CSS-based and can be customized with CSS variables:
|
|
|
442
611
|
{code}
|
|
443
612
|
</ShikiHighlighter>
|
|
444
613
|
|
|
445
|
-
// Hook (
|
|
614
|
+
// Hook (line-number styles inject automatically when enabled)
|
|
446
615
|
const highlightedCode = useShikiHighlighter(code, "javascript", "github-dark", {
|
|
447
616
|
showLineNumbers: true,
|
|
448
617
|
startingLineNumber: 0,
|
|
@@ -451,14 +620,13 @@ const highlightedCode = useShikiHighlighter(code, "javascript", "github-dark", {
|
|
|
451
620
|
```
|
|
452
621
|
|
|
453
622
|
> [!NOTE]
|
|
454
|
-
>
|
|
455
|
-
>
|
|
456
|
-
> ```tsx
|
|
457
|
-
> import 'react-shiki/css';
|
|
458
|
-
> ```
|
|
623
|
+
> No CSS import is needed: the shared highlighting hook injects line-number and highlight styles automatically when those features are enabled, including when used through the component.
|
|
624
|
+
> To customize them, override `.rs-line-number` (line `span`), `.rs-highlighted-line` (line `span`), and `.rs-has-line-numbers` / `.rs-has-highlighted-lines` (container `code` element) in your own CSS.
|
|
459
625
|
|
|
460
626
|
`highlightLineNumbers` uses displayed line numbers, so it works with `startingLineNumber`.
|
|
461
627
|
|
|
628
|
+
Component styles are embedded in the JS and installed automatically when the component mounts — no CSS import or bundler configuration needed. Injection is scoped to usage: component styles tree-shake out of hook-only bundles entirely, while line-number/highlight styles are installed only when those options are enabled. The complete compiled stylesheet is also exported as `react-shiki/css` for build-time CSS pipelines or as an externally loaded fallback. Importing it does not disable automatic runtime injection.
|
|
629
|
+
|
|
462
630
|
Component-internal default classes are namespaced under `rs-*` and written with zero-specificity `:where()` selectors, so they can't be clobbered by CSS resets (e.g. Tailwind preflight) regardless of stylesheet load order, while any rule in your own CSS — even a bare element selector — overrides them.
|
|
463
631
|
|
|
464
632
|
Available CSS variables for customization:
|
|
@@ -630,57 +798,6 @@ const highlightedCode = useShikiHighlighter(code, "tsx", "github-dark", {
|
|
|
630
798
|
});
|
|
631
799
|
```
|
|
632
800
|
|
|
633
|
-
### Output Formats
|
|
634
|
-
|
|
635
|
-
`react-shiki` can return highlighted code in three formats:
|
|
636
|
-
|
|
637
|
-
**React Nodes (Default)** - Rendered as React elements, no `dangerouslySetInnerHTML` required
|
|
638
|
-
```tsx
|
|
639
|
-
// Hook
|
|
640
|
-
const highlightedCode = useShikiHighlighter(code, "tsx", "github-dark");
|
|
641
|
-
|
|
642
|
-
// Component
|
|
643
|
-
<ShikiHighlighter language="tsx" theme="github-dark">
|
|
644
|
-
{code}
|
|
645
|
-
</ShikiHighlighter>
|
|
646
|
-
```
|
|
647
|
-
|
|
648
|
-
**HTML String** - Rendered via `dangerouslySetInnerHTML`
|
|
649
|
-
```tsx
|
|
650
|
-
// Hook (returns HTML string, use dangerouslySetInnerHTML to render)
|
|
651
|
-
const highlightedCode = useShikiHighlighter(code, "tsx", "github-dark", {
|
|
652
|
-
outputFormat: 'html'
|
|
653
|
-
});
|
|
654
|
-
|
|
655
|
-
// Component (automatically uses dangerouslySetInnerHTML when outputFormat is 'html')
|
|
656
|
-
<ShikiHighlighter language="tsx" theme="github-dark" outputFormat="html">
|
|
657
|
-
{code}
|
|
658
|
-
</ShikiHighlighter>
|
|
659
|
-
```
|
|
660
|
-
|
|
661
|
-
**Shiki Tokens (Experimental)** - Hook-only output for custom renderers
|
|
662
|
-
```tsx
|
|
663
|
-
const highlighted = useShikiHighlighter(code, "tsx", "github-dark", {
|
|
664
|
-
outputFormat: "tokens",
|
|
665
|
-
});
|
|
666
|
-
|
|
667
|
-
const rendered = highlighted?.tokens.map((line, i) => (
|
|
668
|
-
<div key={i}>
|
|
669
|
-
{line.map((token, j) => (
|
|
670
|
-
<span key={j} style={{ color: token.color }}>
|
|
671
|
-
{token.content}
|
|
672
|
-
</span>
|
|
673
|
-
))}
|
|
674
|
-
</div>
|
|
675
|
-
));
|
|
676
|
-
```
|
|
677
|
-
|
|
678
|
-
The React and HTML formats spend their rendering work in different phases. HTML output skips per-token React element creation and reconciliation, reducing render-phase overhead for large, one-shot highlights, but each update replaces the code block's entire DOM subtree via `innerHTML`. React output pays for element creation and diffing on every update, but commits only incremental DOM mutations, minimizing DOM churn for frequently re-highlighted code such as streaming LLM output.
|
|
679
|
-
|
|
680
|
-
HTML output hands the highlighted markup to the DOM via `dangerouslySetInnerHTML`, so only use it with trusted code sources. The default React output is the safe choice for untrusted content.
|
|
681
|
-
|
|
682
|
-
Token output is for when you need to own rendering yourself; it is intentionally available on the hook, not the `ShikiHighlighter` component, since react-shiki's markup-based features no longer apply once you control the markup. Note that Shiki's `codeToTokens` does not run transformers or decorations, so markup-producing options, including transformers, decorations, line numbers, and `structure`, have no effect on token output.
|
|
683
|
-
|
|
684
801
|
---
|
|
685
802
|
|
|
686
803
|
Made with ❤️ by [Bassim (AVGVSTVS96)](https://github.com/AVGVSTVS96)
|