<p class="text-sm text-kumo-strong">
<strong>New in v1.10:</strong> Shiki-powered syntax highlighting with lazy
loading. Import from `@cloudflare/kumo/code` to use.
import { Button } from 'kumo-svelte';
<Button variant="primary">Deploy</Button>Overview
A Shiki-powered syntax highlighter. Supports 200+ languages with TextMate
grammars, dual light/dark themes, and lazy loading. Exported from a separate
entry point ( `@cloudflare/kumo/code` ) to avoid bundling Shiki for apps
that don't need it.
Installation
CodeHighlighted is exported from a separate entry point to avoid bundling Shiki for apps that don't need it.
<p class="text-sm text-kumo-strong">
<strong>Important:</strong> Do NOT import from the main `@cloudflare/kumo` entry.
That would pull Shiki into your bundle even if you don't use it.
Basic Usage
Wrap your app with `ShikiProvider` to configure Shiki once.
All `CodeHighlighted` components inside share the same Shiki instance.
export function App() {
return (
<ShikiProvider
engine="javascript"
languages={["tsx", "typescript", "bash", "json"]}
>
{/* All CodeHighlighted components share the same Shiki instance */}
<CodeHighlighted code="const x = 1;" lang="typescript" />
</ShikiProvider>
);
}
Examples
Languages
CodeHighlighted supports 200+ languages through Shiki. Only load the languages you need.
TypeScript
import { Button } from 'kumo-svelte';
<Button variant="primary">Deploy</Button>React / TSX
import { Button } from 'kumo-svelte';
<Button variant="primary">Deploy</Button>Bash / Shell
import { Button } from 'kumo-svelte';
<Button variant="primary">Deploy</Button>JSON
import { Button } from 'kumo-svelte';
<Button variant="primary">Deploy</Button>CSS
import { Button } from 'kumo-svelte';
<Button variant="primary">Deploy</Button>Highlight Lines
Emphasize specific lines with highlightLines (1-indexed).
import { Button } from 'kumo-svelte';
<Button variant="primary">Deploy</Button>Custom Highlight Color
Customize the highlight color with the --kumo-code-highlight-bg CSS
variable.
CodeHighlightedCustomHighlightDemo
Line Numbers
Display line numbers with showLineNumbers .
import { Button } from 'kumo-svelte';
<Button variant="primary">Deploy</Button>Copy Button
Add a copy-to-clipboard button with showCopyButton .
import { Button } from 'kumo-svelte';
<Button variant="primary">Deploy</Button>Full Featured
Combine all features for a complete code display experience.
import { Button } from 'kumo-svelte';
<Button variant="primary">Deploy</Button>Shared Provider
Multiple code blocks can share a single `ShikiProvider`.
Shiki loads once and is reused for all blocks.
import { Button } from 'kumo-svelte';
<Button variant="primary">Deploy</Button>Themes
CodeHighlighted uses hardcoded themes for consistent styling across all Kumo
applications:
- Light mode: `github-light`
- Dark mode: `vesper`
Theme customization is not supported. This ensures visual consistency across all code blocks in your application.
Server-Side Usage
For SSR frameworks (Next.js RSC, Astro, Remix), use the server utilities to highlight at build time.
One-off highlighting
// Next.js RSC or Astro
export default async function Page() {
const html = await highlightCode(`const x = 1;`, "typescript");
return <pre dangerouslySetInnerHTML={{ __html: html }} />;
}
Reusable highlighter
// For multiple highlights, reuse the highlighter
const highlighter = await createServerHighlighter({
languages: ["tsx", "bash", "json"],
});
const html1 = highlighter.highlight(code1, "tsx");
const html2 = highlighter.highlight(code2, "bash");
highlighter.dispose(); // Clean up when done
Custom Hook
Use `useShikiHighlighter` for custom implementations.
function CustomCodeBlock({ code, lang }) {
const { highlight, isLoading, isReady, error } = useShikiHighlighter();
if (error) {
return <div className="text-red-500">Failed to load highlighter</div>;
}
if (isLoading) {
return (
<pre className="animate-pulse">
<code>{code}</code>
</pre>
);
}
const html = highlight(code, lang);
// null means highlighting failed — render plain text
if (html === null) {
return (
<pre>
<code>{code}</code>
</pre>
);
}
return <pre dangerouslySetInnerHTML={{ __html: html }} />;
}
Internationalization
Customize button labels at the provider level for all code blocks, or override per-component.
// Set labels at the provider level for all code blocks
<ShikiProvider
engine="javascript"
languages={["tsx", "bash"]}
labels={{ copy: "Copier", copied: "Copié!" }}
>
<App />
</ShikiProvider>
// Or override at the component level
<CodeHighlighted
code={code}
lang="tsx"
showCopyButton
labels={{ copy: "Copy code", copied: "Done!" }}
/>
Framework Integration
Next.js App Router
// app/providers.tsx
"use client";
export function Providers({ children }) {
return (
<ShikiProvider engine="javascript" languages={["tsx", "bash", "json"]}>
{children}
</ShikiProvider>
);
}
// app/layout.tsx
export default function RootLayout({ children }) {
return (
<html>
<body>
<Providers>{children}</Providers>
</body>
</html>
);
}
Astro (Static)
For static sites, use server-side highlighting for zero client-side JavaScript.
---
// src/components/CodeBlock.astro
const { code, lang } = Astro.props;
const html = await highlightCode(code, lang);
---
<div class="code-block" set:html={html} />
Bundle Size
Shiki is lazy-loaded on first render. The size depends on your
configuration:
<table class="w-full text-sm">
<thead>
<tr class="border-b border-kumo-hairline">
<th class="py-2 pr-4 text-left font-medium">Scenario</th>
<th class="py-2 pr-4 text-left font-medium">Languages</th>
<th class="py-2 pr-4 text-left font-medium">Engine</th>
<th class="py-2 text-left font-medium">Lazy Load Size</th>
</tr>
</thead>
<tbody>
<tr class="border-b border-kumo-hairline/50">
<td class="py-2 pr-4">Minimal</td>
<td class="py-2 pr-4">tsx, json</td>
<td class="py-2 pr-4">JS</td>
<td class="py-2">~75 KB</td>
</tr>
<tr class="border-b border-kumo-hairline/50">
<td class="py-2 pr-4">Standard</td>
<td class="py-2 pr-4">tsx, ts, bash, json, css, yaml</td>
<td class="py-2 pr-4">JS</td>
<td class="py-2">~95 KB</td>
</tr>
<tr>
<td class="py-2 pr-4">Full</td>
<td class="py-2 pr-4">15+ languages</td>
<td class="py-2 pr-4">WASM</td>
<td class="py-2">~250 KB</td>
</tr>
</tbody>
</table>
Teams that don't import from `@cloudflare/kumo/code` pay 0 KB.
Migration from Code/CodeBlock
The legacy `Code` and `CodeBlock` components are deprecated.
They will be removed in v2.0.
// Before (deprecated)
<CodeBlock code={code} lang="tsx" />
// After
// Once at app root
<ShikiProvider engine="javascript" languages={["tsx"]}>
<App />
</ShikiProvider>
// In components
<CodeHighlighted code="const x = 1;" lang="tsx" />
API Reference
ShikiProvider Props
| Prop | Type | Required | Description |
|---|---|---|---|
engine | "javascript" | "wasm" | Yes | JS is smaller (~50KB), WASM is more accurate (~180KB) |
languages | string[] | Yes | Languages to support (e.g., ["tsx", "bash"]) |
labels | { copy?: string, copied?: string } | No | Localized labels for copy button |
children | ReactNode | Yes | App content |
CodeHighlighted Props
| Prop | Type | Required | Description |
|---|---|---|---|
code | string | Yes | Source code to display |
lang | string | Yes | Language identifier (must be in provider's languages) |
showLineNumbers | boolean | No | Display line numbers |
highlightLines | number[] | No | Lines to emphasize (1-indexed) |
showCopyButton | boolean | No | Show copy-to-clipboard button |
labels | { copy?: string, copied?: string } | No | Override provider labels for this instance |
className | string | No | Additional CSS classes |
useShikiHighlighter Return Value
<table class="w-full text-sm">
<thead>
<tr class="border-b border-kumo-hairline">
<th class="py-2 pr-4 text-left font-medium">Property</th>
<th class="py-2 pr-4 text-left font-medium">Type</th>
<th class="py-2 text-left font-medium">Description</th>
</tr>
</thead>
<tbody>
<tr class="border-b border-kumo-hairline/50">
<td class="py-2 pr-4"><code>highlight</code></td>
<td class="py-2 pr-4"><code>(code, lang, options?) => string | null</code></td>
<td class="py-2">Returns highlighted HTML, or null if not ready</td>
</tr>
<tr class="border-b border-kumo-hairline/50">
<td class="py-2 pr-4"><code>isLoading</code></td>
<td class="py-2 pr-4"><code>boolean</code></td>
<td class="py-2">True while Shiki is loading</td>
</tr>
<tr class="border-b border-kumo-hairline/50">
<td class="py-2 pr-4"><code>isReady</code></td>
<td class="py-2 pr-4"><code>boolean</code></td>
<td class="py-2">True when highlight() is safe to call</td>
</tr>
<tr>
<td class="py-2 pr-4"><code>error</code></td>
<td class="py-2 pr-4"><code>Error | null</code></td>
<td class="py-2">Error if Shiki initialization failed</td>
</tr>
</tbody>
</table>