Code Block

The single highlighted-code surface — lazy shiki that costs nothing until code renders, line numbers, an enforced copy control, optional download, and a plain-text fallback for unknown languages.

ts
const TOLERANCE = 0.05;
export function flagDrift(portfolios: Portfolio[]): DriftAlert[] {  return portfolios    .filter((portfolio) => Math.abs(portfolio.drift) > TOLERANCE)    .map((portfolio) => ({      id: portfolio.id,      drift: portfolio.drift,      action: portfolio.drift > 0 ? "trim" : "top-up",    }));}
<CodeBlock code={snippet} language="ts" showLineNumbers />

Usage

import { CodeBlock } from "@reva/ui";

CodeBlock is the single code surface for the whole app — there is no other way to render code, and that is deliberate. Use it for structured code (tool results, snippets), and Response renders every markdown code fence through it too, so a streamed fence and a standalone snippet are the same element, not two surfaces kept in sync.

code is the source (trailing newlines stripped, streamdown-fence parity, before it renders and before it becomes the copy payload), language is the header label plus grammar hint, and showLineNumbers adds a pure-CSS counter gutter. Unknown languages render as plain text — never an error (example below).

Header actions are closed and enforced

The interface is closed — there is no children slot. The header controls are fixed by the component, not composed at the call site:

  • Copy is always present. Every CodeBlock renders the house Clipboard (ghost, Copy → Check feedback, denied-permission fallback) wired to the block's code. You cannot remove it, and you never add it — it is enforced.
  • downloadable adds the one optional action — a ghost download control beside copy that saves the source as snippet.<ext> (derived from language; override with downloadName). Both controls are ghost xs.

This is the point: code blocks cannot drift into per-call-site copy buttons, re-skinned chrome, or one-off action rows. If you need a new code-block action, add it here as a defined prop — never as injected JSX.

<CodeBlock code={snippet} language="ts" showLineNumbers />          {/* copy only */}
<CodeBlock code={report} language="json" downloadable />            {/* copy + download */}

Highlighting

Highlighting runs on a lazy shiki singleton shared across the app: nothing shiki-related loads at import time, so an app that never renders code never downloads the highlighter chunk. The first code render fetches it on demand — core, the JS-regex engine, the theme pair, and the curated grammar set; less-common languages add one further lazy chunk each, fetched the first time a fence asks for them. Consumers that know code is coming can prewarm with the exported getHighlighter:

import { getHighlighter } from "@reva/ui";

useEffect(() => {
  // Prewarm when your chat UI mounts — the first code render in a reply
  // is then highlighted on its first paint.
  void getHighlighter();
}, []);

Rendering is progressive but never flashes once warm: plain code paints immediately (SSR included), highlighted spans swap in when the chunk resolves with no layout shift (both states share the same line structure) — and once the singleton is warm, the sync path highlights during render, so every paint is highlighted on first paint and per-delta re-renders never strobe plain-then-highlighted.

The vitesse theme pair is provisional

Token colours come from shiki's vitesse-light / vitesse-dark — chosen because their olive/brass/rust palette sits naturally with Reva's warm register (the themes' own backgrounds are stripped so the house surface wins in both colour modes). The proper fix is a dedicated syntax-colour family in @reva/design-tokens; this pair gets replaced when that family lands.

Examples

JSON, with download

downloadable adds the optional download control beside the enforced copy.

json
{  "portfolio": "Hargreaves ISA",  "target": { "equity": 0.6, "bonds": 0.35, "cash": 0.05 },  "actual": { "equity": 0.68, "bonds": 0.27, "cash": 0.05 },  "withinTolerance": false}
<CodeBlock code={driftReport} language="json" downloadable />

Unknown-language fallback

A language the registry has no grammar for renders as plain text — the header still labels it, copy still works, and nothing throws. Models invent fence tags often enough that this is the safe default.

mandate-dsl
mandate "hargreaves-isa" {  tolerance 5%  rebalance quarterly when drifted}
<CodeBlock code={mandate} language="mandate-dsl" />

Do's and Don'ts

✅ Do❌ Don't
Render all code — markdown fence or structured part — through CodeBlockHand-roll a code surface, or re-skin Streamdown's default fence chrome
Rely on the built-in copy; reach for downloadable when a block needs savingAdd a bespoke copy button next to a code block (it is already there)
Add a genuinely new action as a defined prop on CodeBlockRe-open the interface with a children slot for one call site's needs

Accessibility

  • The code body is a horizontal scroll region (long lines), so it is keyboard-scrollable: a focusable (tabIndex={0}) named region ("Scrollable code") with an inset focus ring — the same treatment as Conversation's scroll region and Response's markdown tables.
  • The built-in copy is the house Clipboard: a real button named "Copy code" by default (override with copyLabel when several blocks need distinguishing), with Copy → Check feedback and a fallback path for denied clipboard permissions. The optional download control is a labelled ("Download code") ghost IconButton.
  • Token colours are presentation only — the code is real selectable text, and the plain fallback shares the same structure, so selection, copy, and screen-reader output are identical before and after highlighting resolves.

Props

PropTypeDefaultDescription
codestringThe source. Trailing newlines are stripped (streamdown fence parity) before render and before copy. Required.
languagestringHeader label + grammar hint (e.g. "tsx", "python"). Unknown languages render plain, never error. Required.
showLineNumbersbooleanfalseRender the line-number gutter (pure CSS counters).
downloadablebooleanfalseAdd a download action (ghost) beside the always-present copy.
downloadNamestringsnippet.<ext>Filename for the download action; the extension defaults from language (.txt fallback).
copyLabelstring"Copy code"Accessible name for the built-in copy control.

The interface is closed — there is no children slot; copy is enforced and download is the only optional action.

On this page