Skip to content
Kura UI

Code Block

beta

Syntax-highlighted code with a filename header, copy, line numbers, highlighted lines, diffs, wrap, collapsible height and streaming-safe rendering.

@kura-ui/react/code-blockCopy page for agents.mdregistry.json

Snippet

minimal
tsx
import { StreamingText } from "@kura-ui/react/streaming-text";​export function Answer({ stream }: { stream: AsyncIterable<string> }) { // Bursty tokens are evened out into a calm cadence. return <StreamingText stream={stream} speed={90} className="max-w-prose" />;}

File with every option

Filename, line numbers, highlighted lines, controlled wrap, collapsed to 300px, onCopy.

full
app/api/chat/route.tsts
import { anthropic } from "@ai-sdk/anthropic";import { streamText } from "ai";​export const maxDuration = 30;​export async function POST(req: Request) { const { messages } = await req.json();​ const result = streamText({ model: anthropic("claude-opus-5-5"), system: "You are a calm, precise assistant.", messages, temperature: 0.4, });​ return result.toUIMessageStreamResponse();}​// Tokens arrive in bursts; the client smooths them with <StreamingText />.export const runtime = "edge";const RETRIES = 3;const TIMEOUT_MS = 12_000;

wrap: false · copied 0×

Diff

A proposed edit as a unified diff, highlighted as TSX.

variant
components/answer.tsxtsx
@@ -4,7 +4,8 @@ export function Answer() { const [open, setOpen] = React.useState(false);removed: const items = data.filter((d) => d.visible);added: const items = React.useMemo(() => data.filter((d) => d.visible), [data]);added: const reduced = useReducedMotion(); return (removed: <List items={items} animate />added: <List items={items} animate={!reduced} /> );

Streaming

Code arriving a few characters at a time — the open last line never flickers.

state
answer.tsxtsx

Command with a Run action

A bash block with a header Button, followed by a header-less output block.

composition
bash
pnpm dlx @kura-ui/cli add code-blockpnpm --filter web dev --port 3000

Installation

npx shadcn@latest add https://kuro.abstergo.fyi/r/code-block.json

Copies the source into components/kura/code-block with its dependencies (kura-theme, icon-button, use-controllable-state, utils). Tokens come from the kura-theme item.

When to use it

Use it for

  • Show code a model wrote in a chat answer, including while it is still streaming.
  • Show a file or snippet with a filename header and a copy button (docs, READMEs, agent output).
  • Show an agent's proposed edit as a unified diff with +/- lines.
  • Long files that should start collapsed to a fixed height with a “Show N more lines” control.
  • Terminal commands (language="bash") with an extra header action such as Run.

Reach for something else when

  • Inline code inside a sentence — use a plain <code> element.
  • Tool arguments/results JSON inside an agent step — use tool-call, which pretty-prints JSON for you.
  • Prose output from a model — use streaming-text or message.
  • Editable code — use a real editor (CodeMirror/Monaco); CodeBlock is read-only.

Props

PropTypeDefaultDescription
code*string—The source to display. For diffs pass unified-diff lines prefixed with `+`, `-` or a space (`@@`, `+++`, `---` lines render as meta).
languagestring"text"Language id: ts/tsx/js/jsx, json, bash/sh, python, css, html/xml, yaml, sql, go, rust, java, … (see `supportedLanguages`). Unknown ids render as plain text.
filenamestring—File name shown in the header, e.g. "app/page.tsx".
lineNumbersbooleanfalseShow a tabular line-number gutter (not selectable, aria-hidden).
highlightLinesreadonly number[]—1-based line numbers to emphasise with a tinted row and a left rule.
diffboolean—Render `+`/`-` prefixed lines as additions/removals (green/red row, sign gutter, screen-reader “added:/removed:”). Defaults to true when `language` is "diff"; otherwise the content after the sign is highlighted with `language`.
streamingbooleanfalseCode is still arriving. Only complete lines are tokenized (memoized), the unfinished last line renders plain until its newline lands, and a block caret marks the live edge — no flicker from half-open strings or comments.
wrapboolean—Soft-wrap long lines (controlled).
defaultWrapbooleanfalseInitial wrap state when uncontrolled.
onWrapChange(wrap: boolean) => void—Called when the wrap toggle is pressed.
maxHeightnumber—Collapse code taller than this many px, with a bottom fade and a “Show N more lines” pill; the header gets an expand/collapse button.
expandedboolean—Expanded past `maxHeight` (controlled).
defaultExpandedbooleanfalseInitial expanded state when uncontrolled.
onExpandedChange(expanded: boolean) => void—Called when the code is expanded or collapsed.
copyablebooleantrueShow the copy button (icon cross-fades to a check for 1.6s on success).
onCopy(code: string) => void—Called with the copied text after a successful copy.
headerbooleantrueShow the header row (filename/language and actions). Without it, no wrap or copy button is shown.
actionsReact.ReactNode—Extra header actions rendered before wrap/copy, e.g. a ghost xs Button “Run” or “Apply”.

All native attributes of the root element, className and ref pass through.

Anatomy

  1. CodeBlockRoot. `data-language`, `data-wrap`, `data-streaming`, `data-state="collapsed" | "expanded"` (only when collapsible).data-slot="code-block"
  2. HeaderFilename/language and actions.data-slot="code-block-header"
  3. TitleFilename, or the language when no filename is given.data-slot="code-block-title"
  4. LanguageLanguage badge, shown next to a filename.data-slot="code-block-language"
  5. BodyHeight-limited wrapper (transitions max-height when expanding).data-slot="code-block-body"
  6. CodeThe <code> element holding the lines.data-slot="code-block-code"
  7. Line`data-line`, `data-highlighted`, `data-diff="add" | "remove" | "context" | "meta"`.data-slot="code-block-line"
  8. LineNumberGutter number (aria-hidden).data-slot="code-block-line-number"
  9. CaretStreaming caret (aria-hidden).data-slot="code-block-caret"
  10. ExpandBottom fade + “Show N more lines” pill while collapsed.data-slot="code-block-expand"

Accessibility

  • Role: region
  • The scroll area is a focusable `role="region"` labelled “<filename or language> code”, so keyboard users can scroll wide code with the arrow keys.
  • Copy, wrap and expand are labelled icon buttons with tooltips; wrap exposes `aria-pressed`, expand exposes `aria-expanded`.
  • A polite status region announces “Copied to clipboard”.
  • Line numbers, diff signs and the caret are aria-hidden; removed/added lines get visually hidden “removed:”/“added:” prefixes.
  • Syntax colours derive from --kura-fg-muted, keeping its ≥ 4.5:1 contrast in both themes.
  • Reduced motion: The copy icon swap becomes a pure cross-fade (no scale/blur), the streaming caret stops pulsing and expanding is instant.
KeysAction
TabMoves through header actions, then the scrollable code region
← / →Scrolls wide code horizontally when the region is focused
Enter / SpaceActivates copy, wrap or expand

Notes for agents

  • Highlighting is built in, synchronous and dependency-free (no shiki/prism needed); unknown languages render as plain text.
  • While a model is still writing code, pass `streaming` and the growing string as `code`; set it to false when the fence closes.
  • For diffs keep the leading `+`/`-`/space on every line and set `diff` (or language="diff" for no inner highlighting).
  • Theme the palette with CSS variables on any ancestor: --kura-syntax-keyword, -string, -constant, -function, -type, -tag, -attr, -property.
  • `highlightCode(code, language)` is exported for custom renderers; it returns tokens grouped by line.