Code block

Code with line numbers, highlighting, and a copy button, highlighted on the server so it paints in color.

use-search.ts
Fig. 1 A highlighted file with its language, display options, and copy button.

When to use it

Use Code Block for any code longer than a line. CodeBlockFile highlights it with Pierreโ€™s themes on the JZB code surface.

For a word or two of code in a sentence, use inline code instead.

Examples

Terminal
$ pnpm build
> vite build

โœ“ 1284 modules transformed.
dist/index.html                0.46 kB
dist/assets/index-4f2a9c.css  18.92 kB
dist/assets/index-b81e07.js  142.31 kB
โœ“ built in 2.41s
Fig. 2 Output. Pass a string to CodeBlockContent for terminal output and logs. It renders as one text node, without loading the highlighter, so it stays cheap while output streams.
code-block-output.tsx
greeting.tsx
export function Greeting({ name }: { name: string }) {  return <p>Hello, {name}</p>  return <p className="body-md">Hello, {name}!</p>}
Fig. 3 Changed lines. Render each line as a CodeBlockLine to mark it added, removed, or highlighted. Added and removed lines carry a + or โˆ’ marker as well as a tint. For real diffs, use the @pierre/diffs FileDiff that CodeBlockFile is built on.
code-block-lines.tsx
app.tsx
Fig. 4 Highlighted lines. Pass highlightLines to CodeBlockFile to call out lines by number. Their numbers take the accent with a bar beside them, and the tint fades across the line. preloadCodeBlock highlights them on the server too.
code-block-highlight.tsx
use-search.ts
Fig. 5 Max height. Wrap CodeBlockFile in a ScrollArea with a max-h-* class to cap a long file. Thin scrollbars appear on the area's edges while the pointer is over the code or it scrolls, including the horizontal one for long lines.
code-block-max-height.tsx
Build failed
TypeError: Cannot read properties of undefined (reading 'map')
    at ProjectList (src/components/project-list.tsx:18:22)
    at renderWithHooks (react-dom.development.js:16305:18)
Fig. 6 Error. Use variant="destructive" for failed output, and size="sm" for tighter padding in tool results. Add wrap so long messages stay readable.
code-block-error.tsx
Request log
[01] GET /api/projects 200 in 12ms
[02] GET /api/projects 200 in 19ms
[03] GET /api/projects 200 in 26ms
[04] GET /api/projects 200 in 33ms
[05] GET /api/projects 200 in 40ms
[06] GET /api/projects 200 in 47ms
[07] GET /api/projects 200 in 14ms
[08] GET /api/projects 200 in 21ms
[09] GET /api/projects 200 in 28ms
[10] GET /api/projects 200 in 35ms
[11] GET /api/projects 200 in 42ms
[12] GET /api/projects 200 in 49ms
[13] GET /api/projects 200 in 16ms
[14] GET /api/projects 200 in 23ms
[15] GET /api/projects 200 in 30ms
[16] GET /api/projects 200 in 37ms
[17] GET /api/projects 200 in 44ms
[18] GET /api/projects 200 in 51ms
[19] GET /api/projects 200 in 18ms
[20] GET /api/projects 200 in 25ms
[21] GET /api/projects 200 in 32ms
[22] GET /api/projects 200 in 39ms
[23] GET /api/projects 200 in 46ms
[24] GET /api/projects 200 in 13ms
Fig. 7 Show more. CodeBlockFooter sits below the content. Cap the content while it is collapsed; its edges fade as it scrolls.
code-block-show-more.tsx

How it behaves

Compose the block from parts: CodeBlockHeader holds a CodeBlockTitle and CodeBlockActions, and CodeBlockFooter sits below the content. Without a header, put CodeBlockActions directly in CodeBlock: they float at the top end, show on hover or keyboard focus, stay while one of their menus is open, and always show on touch screens.

CodeBlockFile renders highlighted code with @pierre/diffs. Pass code with a language, or a filename to infer one. Pierre Light (light mode) and Pierre Dark Soft (dark mode) color the tokens; pass theme with any Pierre variant or Shiki theme. The surface, border, code font, and size stay on the JZB code tokens. Pass highlightLines to call out lines by number, and wrap the file in ScrollArea with a max-h-* class to cap a long one.

CodeBlockContent renders plain text in a pre without loading the highlighter, for terminal output, logs, and streaming text. Add lineNumbers and startLine to number it, wrap to wrap long lines, or a max-h class to cap it; its edges fade as it scrolls. Render CodeBlockLine children to mark lines added, removed, or highlighted; each tint fades across the line, and highlighted lines match CodeBlockFile's accent bar.

CodeBlockLanguageSelect and CodeBlockDisplayMenu are optional actions for a CodeBlockFile; keep their state yourself and pass it to the file. CodeBlockCopyButton copies its value, shows a tooltip, and announces Copied in a polite live region.

Code always reads left to right. CodeBlockContent is focusable so keyboard users can scroll it, and line numbers and diff markers are left out of a copied selection.

Without prerendered HTML, CodeBlockFile shows numbered plain code until the highlighter loads in the browser. Highlight on the server with preloadCodeBlock to paint colors on the first load; see Highlight on the server above. While a new language or display setting loads, prerendered code keeps its current view instead of flashing plain.

Build it

Add it with the shadcn CLI:

Terminal

A complete example. Change the values to fit your product:

code-block-example.tsx

Highlight on the server

On its own, CodeBlockFile highlights in the browser once its JavaScript has loaded. Until then it shows the code with line numbers but without colors. To paint highlighted code on the first load, highlight it on the server with preloadCodeBlock and pass the result as prerenderedHTML. This page does that while it is prerendered.

TanStack Start ยท routes/guide.tsx
Next.js ยท app/guide/page.tsx

Pass the same settings to both: code, language or filename, line numbers, wrapping, and theme. Different settings canโ€™t reuse the prerendered markup, and client-side navigation without it falls back to highlighting in the browser.

The full source is yours to edit once installed:

code-block.tsx