Skip to content
Redpanda UI
Esc
↑↓navigate↵open⌘Jpreview
Made by shadcn
Edit on GitHub

Code Block

Displays code, terminal output, logs, and diffs, with optional syntax highlighting.

Powered by

Installation

bash
npx shadcn@latest add @redpanda/code-block
bash
pnpm dlx shadcn@latest add @redpanda/code-block
bash
yarn dlx shadcn@latest add @redpanda/code-block
bash
bunx shadcn@latest add @redpanda/code-block
bash
nubx shadcn@latest add @redpanda/code-block
bash
aube dlx shadcn@latest add @redpanda/code-block

The install brings the useHighlightedCode hook and the highlighter library with it. Highlighting is optional: a block renders any string you give it.

Usage

tsx
import {  CodeBlock,  CodeBlockActions,  CodeBlockContent,  CodeBlockCopyButton,  CodeBlockHeader,  CodeBlockTitle,} from '@/components/redpanda-ui/components/code-block';
tsx
<CodeBlock>  <CodeBlockHeader>    <CodeBlockTitle>Terminal</CodeBlockTitle>    <CodeBlockActions>      <CodeBlockCopyButton value="rpk topic list" />    </CodeBlockActions>  </CodeBlockHeader>  <CodeBlockContent>rpk topic list</CodeBlockContent></CodeBlock>

For a block built from one string, use SimpleCodeBlock.

Anatomy

text
CodeBlock├── CodeBlockHeader│   ├── CodeBlockTitle│   └── CodeBlockActions│       ├── CodeBlockWrapButton│       └── CodeBlockCopyButton├── CodeBlockContent│   └── CodeBlockLines (or CodeBlockLine)└── CodeBlockFooter

Without a header, CodeBlockActions floats over the top end corner of the block. Its default visibility="hover" reveals controls on pointer hover, keyboard focus, or coarse-pointer devices. Use visibility="always" for persistent floating controls; SimpleCodeBlock selects this recipe when it has no title. Copy errors remain visible until the next attempt in either mode.

When to use

  • Use CodeBlock for code, commands, configuration, logs, stack traces, and diffs that people read and copy.
  • Use Code Tabs for the same snippet in several package managers or languages.
  • Use Code Editor when people edit the code.
  • Use an inline <code> for a name or a short command inside a sentence.

Syntax highlighting

CodeBlockLines turns a string into CodeBlockLines, highlighted when you give it a language:

tsx
<CodeBlock>  <CodeBlockContent>    <CodeBlockLines code={code} language="ts" />  </CodeBlockContent></CodeBlock>

It keys each line by where it starts in the code, so a finished line keeps its node while code streams in, and no call site needs an index key. To render the lines yourself, call useHighlightedCode(code, language), which returns one React node per line, and put each one in a CodeBlockLine.

  • Languages load on first use. The list lives in lib/highlighter: bash, css, diff, go, html, java, javascript, json, jsx, log, markdown, powershell, protobuf, python, rust, sql, toml, tsx, typescript, and yaml, plus aliases such as ts, js, sh, yml, rs, and proto. A language-* class from a Markdown fence works too. Add a language by adding one line to that list.
  • An unlisted language renders as plain text, with no highlighter loaded for it.
  • "ansi" renders terminal colours from ANSI escape codes. Colours do not carry across lines.
  • Streaming is cheap. As code arrives, only the new lines are tokenized, and finished lines keep their nodes.
  • On the server, await highlightCode(code, language) from @/lib/highlighter returns the same lines and keeps Shiki out of the client bundle.

Highlighting runs Shiki on its JavaScript regex engine, so no WASM loads. Shiki’s core, engine, themes, and grammars load with the first block that highlights. Tokens use github-light-high-contrast and github-dark-default; the theme’s code-token utility selects the palette for the surrounding surface, including terminal blocks.

Examples

Simple code block

SimpleCodeBlock builds the whole block from one string: highlighting, a header when there is a title, and wrap and copy buttons. Without a title, the buttons float over the block and stay visible, as the copy button did before the parts. wrap sets whether lines start wrapped, and allowWrap={false} hides the wrap toggle.

tsx
<SimpleCodeBlock code={query} language="sql" lineNumbers title="top-customers.sql" />

Floating actions

Without a header, the actions appear on hover or focus, and stay shown on touch screens.

Plain

Line numbers

lineNumbers numbers each CodeBlockLine, and startLine sets the first number. Numbers are a CSS counter, so they are never part of copied or selected text, and screen readers skip them. Put a string’s lines in with CodeBlockLines: unlike Shadcn UI’s block, CodeBlockContent does not split a string child itself.

Diff and highlighted lines

Set variant on a line to added, removed, or highlighted, or give CodeBlockLines a variants map by line number. Added and removed lines carry a + or − marker, so the change never depends on colour alone.

tsx
<CodeBlockLines code={code} language="ts" variants={{ 2: 'removed', 3: 'added' }} />

Wrap

wrap wraps long lines instead of scrolling them. CodeBlockWrapButton is the toggle for it, a Button with aria-pressed. It changes the display only: copying keeps the source’s own line breaks.

Show more

Cap the content with a max-h-* class, and toggle it from a Button in CodeBlockFooter. Give the button aria-expanded, and point its aria-controls at the content’s id.

Terminal

variant="terminal" keeps the block dark in both app themes. It sets data-theme="dark" on the block, so its tokens and highlighting both resolve to the dark theme. Pair it with size="sm" and the "ansi" language for command output.

Destructive

variant="destructive" is for errors and failed commands.

Redpanda examples

Client languages

The same order produced to the orders topic from Go (franz-go), Python (confluent-kafka), Java, TypeScript (KafkaJS), and Rust (rdkafka). For the same step in several languages, put one block per language in a TabsContent.

Schema Registry

A topic’s value schema in Protobuf and Avro, with line numbers, and the rpk registry commands that register it in a terminal block.

Redpanda Connect

A pipeline that reads orders, enriches each record with a Bloblang mapping, and writes orders-enriched. The mapping lines are highlighted through CodeBlockLines variants.

rpk

A session that points rpk at a local cluster, then creates, produces to, and consumes from a topic.

Accessibility

  • CodeBlockContent renders a pre with a code element, always left to right, while the header follows the page direction.
  • The content takes focus while it scrolls, so keyboard users can scroll a long line or a capped block. A block that fits is not a tab stop, so a page of short commands is not a page of tab stops. Pass tabIndex to decide yourself.
  • Floating actions show on focus as well as hover.
  • CodeBlockCopyButton keeps the name “Copy”, and a status region announces “Copied”. The result is announced once, not again by a renamed button.
  • While the write is pending, the button stays focused but ignores clicks. When the clipboard refuses the write, an alert reads “Could not copy code. Try again.” and stays, on a row of its own that fits a narrow block, until the next try succeeds.
  • Line numbers are hidden from screen readers, and diff markers back up their colour.

API reference

CodeBlock

Also takes every div prop.

PropType
variant?"default" | "destructive" | "terminal"

The tone of the block. `terminal` stays dark in both app themes.

Type"default" | "destructive" | "terminal"
Default"default"
size?"default" | "sm"

Tighter spacing and smaller text, for tool and log output.

Type"default" | "sm"
Default"default"
testId?string

`data-testid` for the block.

Typestring

CodeBlockCopyButton

Also takes every Button prop. It is a ghost icon-xs button by default.

PropType
valuestring | (() => string)

The text to copy, or a function that returns it at the moment of the click.

Typestring | (() => string)
onCopied?() => void

Called once the text is on the clipboard.

Type() => void
onClick?(event) => void

Runs before the copy. Call `event.preventDefault()` to skip it.

Type(event) => void
copyLabel?string

The button's name, which stays put through copying.

Typestring
Default"Copy"
copiedLabel?string

Announced once the text is on the clipboard.

Typestring
Default"Copied"
copyFailedLabel?string

Shown until the next try, when the clipboard refuses the write.

Typestring
Default"Could not copy code. Try again."

CodeBlockWrapButton

Also takes every Button prop. It is a ghost icon-xs button by default.

PropType
wrappedboolean

Whether the content wraps now. Pass it to `CodeBlockContent` as `wrap`.

Typeboolean
onWrappedChange(wrapped: boolean) => void

Called with the next wrap state on click.

Type(wrapped: boolean) => void
label?string

The button's name.

Typestring
Default"Wrap lines"

CodeBlockContent

Also takes every pre prop. Cap its height with a max-h-* class.

PropType
children?React.ReactNode

A string, `CodeBlockLines`, or `CodeBlockLine` elements.

TypeReact.ReactNode
wrap?boolean

Wrap long lines instead of scrolling them.

Typeboolean
Defaultfalse
lineNumbers?boolean

Number each `CodeBlockLine`.

Typeboolean
Defaultfalse
startLine?number

The number of the first line, with `lineNumbers`.

Typenumber
Default1
tabIndex?number

Overrides the default, which is focusable only while the content scrolls.

Typenumber
Default0 while it scrolls

CodeBlockLines

PropType
codestring

The code, split into one `CodeBlockLine` per line. A trailing newline is dropped.

Typestring
language?string

Highlights the lines. Without one, or for an unlisted language, they stay plain.

Typestring
variants?Partial<Record<number, "default" | "added" | "removed" | "highlighted">>

A line's variant, by its line in `code` counting from 1, whatever the content's `startLine`.

TypePartial<Record<number, "default" | "added" | "removed" | "highlighted">>

CodeBlockLine

Also takes every span prop. An empty line keeps a newline, so a copied selection keeps blank lines.

PropType
variant?"default" | "added" | "removed" | "highlighted"

Marks the line in a diff, or draws attention to it.

Type"default" | "added" | "removed" | "highlighted"
Default"default"

CodeBlockHeader, CodeBlockTitle, CodeBlockActions, and CodeBlockFooter take every div prop. CodeBlockTitle truncates, and sizes an icon placed before the text. CodeBlockActions also accepts visibility="hover" | "always" (default: "hover").

SimpleCodeBlock

PropType
codestring

The code to show and copy.

Typestring
language?string

Highlights the code. An unlisted language renders as plain text.

Typestring
title?React.ReactNode

Shows a header with this title.

TypeReact.ReactNode
icon?React.ReactNode

Shown before the title.

TypeReact.ReactNode
allowCopy?boolean

Show the copy button.

Typeboolean
Defaulttrue
allowWrap?boolean

Show the wrap toggle. Copying keeps the source either way.

Typeboolean
Defaulttrue
onCopy?() => void

Called once the code is on the clipboard.

Type() => void
lineNumbers?boolean

Number each line.

Typeboolean
Defaultfalse
wrap?boolean

Whether long lines start wrapped. The wrap toggle changes it from there.

Typeboolean
Defaultfalse
variant?"default" | "destructive" | "terminal"

The tone of the block.

Type"default" | "destructive" | "terminal"
Default"default"
size?"default" | "sm"

Use default or compact spacing.

Type"default" | "sm"
Default"default"
width?"auto" | "sm" | "md" | "lg" | "full"

Caps the width of the block.

Type"auto" | "sm" | "md" | "lg" | "full"
Default"md"
maxHeight?"sm" | "md" | "lg" | "none"

Caps the content at 300, 600, or 800 pixels, after which it scrolls.

Type"sm" | "md" | "lg" | "none"
Default"md"
testId?string

`data-testid` for the block.

Typestring
copyTestId?string

`data-testid` for the copy button. Defaults to `${testId}-copy` when `testId` is set.

Typestring

Migrating from the previous CodeBlock

CodeBlock is now a set of parts, matching Shadcn UI’s code block. SimpleCodeBlock keeps its props, so most call sites need no change. They gain syntax highlighting and a wrap toggle, which allowWrap={false} hides.

Before After
<CodeBlock title="x" icon={i}> <CodeBlockHeader><CodeBlockTitle>{i}x</CodeBlockTitle></CodeBlockHeader>
allowCopy (on by default) Add <CodeBlockCopyButton value={code} /> inside CodeBlockActions. Leave it out for no button.
onCopy onCopied on CodeBlockCopyButton
copyTestId testId on CodeBlockCopyButton
<Pre><code>{code}</code></Pre> <CodeBlockContent>{code}</CodeBlockContent>, or <CodeBlockLines code={code} /> inside it for line numbers and highlighting
size="md" or size="lg" Leave size out. size="sm" stays.
width="full" Leave it out: the block fills its container. Cap it with a max-w-* class.
maxHeight="sm" A max-h-* class on CodeBlockContent
viewportProps Props on CodeBlockContent
A string icon holding HTML An icon element
The block’s outer margin (my-3 to my-8) None: space the block from its container.

Migrating from Code Block (Dynamic)

Code Block (Dynamic) was removed in this breaking release. Use this component instead.

Before After
<DynamicCodeBlock code={code} lang="tsx" /> <SimpleCodeBlock code={code} language="tsx" />
<SyncCodeBlock code={code} lang="bash" /> <SimpleCodeBlock code={code} language="bash" />
themes={TERMINAL_THEMES} with keepBackground variant="terminal"
title, icon, allowCopy, testId, copyTestId The same props on SimpleCodeBlock
onCopied onCopy on SimpleCodeBlock, or onCopied on CodeBlockCopyButton
options with custom Shiki themes, transformers, or components Compose the parts, and render your own lines in CodeBlockLine
data-line-numbers and data-line-numbers-start lineNumbers and startLine on CodeBlockContent, around CodeBlockLines

Credits

Recent changes

  • patchv1.2.0Pin shipped dependency floors to the version we develop against. Registry items now declare ranges like `^5.1.9` (the actual installed version) instead of collapsing to `^5.0.0`, so consumers start on the known-tested baseline while caret semantics still allow any compatible release within the same major.#133
  • minorv1.1.0Theme docs refresh, readability pass on semantic foregrounds, and consumer-facing Base UI regression fixes.#121
  • minorv1.0.0Post-Base-UI polish. Public API unchanged.#116
  • majorv1.0.0Migrate every Radix-based primitive to `@base-ui/react@^1.4.0` (Base UI).#114
  • minorv0.3.0Add theme-provider component to the registry with documentation and tests. Includes playground type improvements (export RegistryItem, remove as-const boilerplate) and docs site dark mode border color fix.#109
See full history →

Last updated on

Was this page helpful?