CodeHighlighted
kumo-svelte
<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

PropTypeRequiredDescription
engine"javascript" | "wasm"YesJS is smaller (~50KB), WASM is more accurate (~180KB)
languagesstring[]YesLanguages to support (e.g., ["tsx", "bash"])
labels{ copy?: string, copied?: string }NoLocalized labels for copy button
childrenReactNodeYesApp content

CodeHighlighted Props

PropTypeRequiredDescription
codestringYesSource code to display
langstringYesLanguage identifier (must be in provider's languages)
showLineNumbersbooleanNoDisplay line numbers
highlightLinesnumber[]NoLines to emphasize (1-indexed)
showCopyButtonbooleanNoShow copy-to-clipboard button
labels{ copy?: string, copied?: string }NoOverride provider labels for this instance
classNamestringNoAdditional 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?) =&gt; 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>