useHighlightedCode
Highlights code with Shiki, one React node per line, for a Code Block.
Installation
npx shadcn@latest add @redpanda/use-highlighted-codepnpm dlx shadcn@latest add @redpanda/use-highlighted-codeyarn dlx shadcn@latest add @redpanda/use-highlighted-codebunx shadcn@latest add @redpanda/use-highlighted-codenubx shadcn@latest add @redpanda/use-highlighted-codeaube dlx shadcn@latest add @redpanda/use-highlighted-codeThe install brings the highlighter library with it. Code Block installs both.
useHighlightedCode(code, language) returns one React node per line. Each one goes in a CodeBlockLine. CodeBlockLines calls it for you and keys the lines, so reach for the hook when you render the lines yourself. Every token carries both themes’ colours, and the theme’s code-token utility picks the one for the theme around it, so update @redpanda/theme along with the hook.
Usage
"use client"
import {
CodeBlock,
CodeBlockContent,
CodeBlockLine,
} from "@/components/redpanda-ui/components/code-block"
import { useHighlightedCode } from "@/components/redpanda-ui/lib/use-highlighted-code"
export function Snippet({ code }: { code: string }) {
const lines = useHighlightedCode(code, "yaml")
return (
<CodeBlock>
<CodeBlockContent lineNumbers>
{lines.map((line, index) => (
<CodeBlockLine key={index}>{line}</CodeBlockLine>
))}
</CodeBlockContent>
</CodeBlock>
)
}
Behavior
- Plain lines come first. The hook returns the code split into lines until the language loads, so the block keeps its size when the colours arrive.
- Each language loads on first use, as its own chunk. An unlisted language stays plain, and loads nothing.
- A trailing newline is ignored, so it does not render as an empty last line.
- Streaming only tokenizes new lines. When the code grows, the finished lines keep their nodes, and only the lines after them are tokenized, from the grammar state the last finished line left.
"ansi"renders terminal colours from ANSI escape codes, one line at a time.
Languages
The list lives in lib/highlighter: bash, css, diff, go, html, java, javascript, json, jsx, log, markdown, powershell, protobuf, python, rust, sql, toml, tsx, typescript, and yaml. Aliases include ts, js, sh, shell, zsh, yml, md, py, rs, proto, ps1, and golang, and a language-* class from a Markdown fence resolves too. Add a language with one line:
const languages = {
// ...
kotlin: () => import("shiki/langs/kotlin.mjs"),
}
On the server
highlightCode(code, language) from lib/highlighter is async, so a Server Component can highlight and keep Shiki out of the client bundle. It returns the same lines, or null for an unlisted language.
import { highlightCode } from "@/components/redpanda-ui/lib/highlighter"
const lines = (await highlightCode(code, "sql")) ?? code.split("\n")
API
codestring
The code to highlight. It may grow between renders as it streams.
stringlanguagestring
A listed language or alias, a `language-*` class, or `"ansi"`. Anything else stays plain.
stringReturns React.ReactNode[], one node per line.
Credits
- Based on the
useHighlightedCodehook from the Shadcn UI Code Block.