Code Block
betaSyntax-highlighted code with a filename header, copy, line numbers, highlighted lines, diffs, wrap, collapsible height and streaming-safe rendering.
Snippet
minimal
tsx
File with every option
Filename, line numbers, highlighted lines, controlled wrap, collapsed to 300px, onCopy.
full
app/api/chat/route.tsts
wrap: false · copied 0×
Diff
A proposed edit as a unified diff, highlighted as TSX.
variant
components/answer.tsxtsx
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
Installation
npx shadcn@latest add https://kuro.abstergo.fyi/r/code-block.jsonCopies 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
| Prop | Type | Default | Description |
|---|---|---|---|
| code* | string | — | The source to display. For diffs pass unified-diff lines prefixed with `+`, `-` or a space (`@@`, `+++`, `---` lines render as meta). |
| language | string | "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. |
| filename | string | — | File name shown in the header, e.g. "app/page.tsx". |
| lineNumbers | boolean | false | Show a tabular line-number gutter (not selectable, aria-hidden). |
| highlightLines | readonly number[] | — | 1-based line numbers to emphasise with a tinted row and a left rule. |
| diff | boolean | — | 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`. |
| streaming | boolean | false | Code 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. |
| wrap | boolean | — | Soft-wrap long lines (controlled). |
| defaultWrap | boolean | false | Initial wrap state when uncontrolled. |
| onWrapChange | (wrap: boolean) => void | — | Called when the wrap toggle is pressed. |
| maxHeight | number | — | 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. |
| expanded | boolean | — | Expanded past `maxHeight` (controlled). |
| defaultExpanded | boolean | false | Initial expanded state when uncontrolled. |
| onExpandedChange | (expanded: boolean) => void | — | Called when the code is expanded or collapsed. |
| copyable | boolean | true | Show 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. |
| header | boolean | true | Show the header row (filename/language and actions). Without it, no wrap or copy button is shown. |
| actions | React.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
- CodeBlockRoot. `data-language`, `data-wrap`, `data-streaming`, `data-state="collapsed" | "expanded"` (only when collapsible).
data-slot="code-block" - HeaderFilename/language and actions.
data-slot="code-block-header" - TitleFilename, or the language when no filename is given.
data-slot="code-block-title" - LanguageLanguage badge, shown next to a filename.
data-slot="code-block-language" - BodyHeight-limited wrapper (transitions max-height when expanding).
data-slot="code-block-body" - CodeThe <code> element holding the lines.
data-slot="code-block-code" - Line`data-line`, `data-highlighted`, `data-diff="add" | "remove" | "context" | "meta"`.
data-slot="code-block-line" - LineNumberGutter number (aria-hidden).
data-slot="code-block-line-number" - CaretStreaming caret (aria-hidden).
data-slot="code-block-caret" - 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.
| Keys | Action |
|---|---|
| Tab | Moves through header actions, then the scrollable code region |
| ← / → | Scrolls wide code horizontally when the region is focused |
| Enter / Space | Activates 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.