Code block
Restrained syntax highlighting that keeps up with streaming, with copy, wrap, line numbers, highlighted lines, long-block collapse and multi-file tabs.
On this page
Installation
Usage
import { CodeBlock } from "@/components/ui/chatcn/code-block"
<CodeBlock code={source} language="ts" filename="middleware.ts" showLineNumbers highlightLines="9-13" />Highlighting uses one shared Shiki instance that loads with the first code block, then one grammar per language. Until a grammar arrives the code shows as plain text with the same metrics, so nothing moves when color arrives.
Examples
Streaming
With streaming, finished lines are tokenized once and kept; only the line being written is re-tokenized, continuing from the saved grammar state.
Files
Long blocks
Blocks over collapseAfter lines (24 by default) collapse behind Show N more lines. A block that streamed in never collapses, so the reader keeps their place.
Languages
Theme
Tokens carry a role, and each role reads one variable: --chatcn-syntax-keyword (red), -string (deep blue), -constant (blue), -function (purple), -type (amber), -tag (green), with comments and punctuation on the gray ramp. Light and dark have their own tuned values. Override the variables in your theme to restyle every block, including the docs-style blocks rendered on the server.
Prefer gray code? Add chatcn-code-mono to <html> or to any container. Inside it, every role maps to a gray step instead of a hue.
Props
| Prop | Type | Notes |
|---|---|---|
code | string | Source text |
language | string | Aliases such as ts, sh and py resolve |
filename | string | Shown instead of the language |
streaming | boolean | Incremental highlighting and the caret |
showLineNumbers, startLine | boolean, number | Sticky gutter |
highlightLines | number[] | "3-5,9" | Marked lines |
wrap, defaultWrap, onWrapChange | Controlled or uncontrolled wrap | |
collapseAfter | number | false | Default 24 |
actions | ReactNode | Extra header actions, before Copy |
hideHeader | boolean | Body only |
CodeBlockTabs takes files: { name, code, language?, streaming?, highlightLines? }[], plus value, defaultValue and onValueChange. The language comes from the file extension when omitted.
Keyboard
| Key | Action |
|---|---|
Tab | Focus the code (to scroll it), wrap, copy and Show more |
Arrow keys | Scroll the focused code, or move between file tabs |
Accessibility
- The code is a labeled, focusable region (for example TypeScript code), always left to right.
- Wrap is a toggle button with
aria-pressed. Copy announces Copied. - Line numbers are hidden from screen readers and from text selection.
Motion
- The copy icon swaps to a check with a 150 ms blur crossfade.
- Expanding a long block is instant: it is a large change, and animating height would only delay reading.