suiss/ui · Component
Code Block
A surface for code, terminal output and diffs, with copy, line numbers, line states and streaming syntax highlighting.
Code Block is an open-source component from suiss/ui for React, React Native, SwiftUI and Jetpack Compose. The @esuiss/ui CLI copies its source into your project, so you own the code.
CodeBlock shows code and command output in a rounded surface. Put the code in CodeBlockContent and, optionally, a CodeBlockHeader with a title and actions above it and a CodeBlockFooter below it.
Pass a string to CodeBlockContent for plain output, or a list of CodeBlockLine when lines need a state or highlighted tokens. useHighlightedCode highlights with Shiki and keeps working while the code streams in.
Installation
CLI
Works in any project with a components.json. See Installation for the one-line registry setup. Or use the suiss/ui CLI:
Manual
- Copy the source into
components/ui/code-block.tsx:
- For syntax highlighting, copy the highlighter into
lib/highlighter.tsx:
- Copy the hook into
hooks/use-highlighted-code.ts:
- Update the import paths to match your project.
Usage
A string child renders as a single text node, so streamed output stays cheap. Line numbers and diff markers are left out of a selection, and the content always reads left to right, while the header follows the page direction.
Examples
Basic
A string passed straight to CodeBlockContent.
Header
A CodeBlockHeader with a title and a copy button. Long titles truncate.
Actions
Extra buttons next to the copy button. The wrap toggle keeps its own state and passes it to wrap.
Floating actions
Without a header, place CodeBlockActions directly in CodeBlock. The actions float at the top end and appear on hover or focus, and always on touch screens.
Line numbers
Set lineNumbers on CodeBlockContent. A string child is split into lines.
Start line
Set startLine to number a snippet from the middle of a file. The gutter widens to fit the largest number.
Wrap
Lines scroll by default. Set wrap for tool output and error messages.
Highlighted lines
Render each line as a CodeBlockLine and set variant="highlighted" on the ones to call out.
Diff
variant="added" and variant="removed" tint a line and add a + or − marker.
Height
A max-h-* class on CodeBlockContent caps the block. The content scrolls inside and its edges fade with scroll-fade.
Show more
Cap the content while collapsed and toggle it from a ghost button in CodeBlockFooter.
Show more with max height
Raise the cap instead of removing it, so a long expanded block still scrolls inside the page.
Size
size="sm" tightens the padding and uses text-xs, for tool and trace output.
Destructive
variant="destructive" for errors and failed output.
Trace
A small, wrapping CodeBlock inside a collapsible trace step for terminal output.
Tabs
Several files in Tabs, one CodeBlock per panel.
Message
A CodeBlock between paragraphs of a Message.
Syntax highlighting
useHighlightedCode returns one React node per line, ready for CodeBlockLine.
Shiki runs on its JavaScript regex engine, so there is no WASM to load.
Languages
Each language loads on first use. The list lives in lib/highlighter.tsx and ships with bash, css, diff, html, javascript, json, jsx, markdown, python, sql, tsx, typescript and yaml, plus aliases such as ts, js, sh and yml. Any other language renders as plain text.
Terminal output
Pass "ansi" as the language to turn escape codes into colors. Colors do not carry over from one line to the next.
Streaming
While code streams in, the hook tokenizes only the new lines. Unchanged lines keep their nodes, so React skips them.
Server rendering
highlightCode is async and runs in a Server Component, which keeps Shiki out of the client bundle.
API Reference
CodeBlock
The surface that holds the header, the content and the footer.
| Prop | Type | Default | Description |
|---|---|---|---|
variant | "default" | "destructive" | "default" | The tone of the block. |
size | "default" | "sm" | "default" | sm uses tighter padding and text-xs, for tool output. |
Also accepts the props of React.ComponentProps<"div">.
CodeBlockHeader
A row above the content for a title and actions.
Also accepts the props of React.ComponentProps<"div">.
CodeBlockTitle
A filename or label with an optional icon. It truncates.
Also accepts the props of React.ComponentProps<"div">.
CodeBlockActions
Buttons at the end of the header. As a direct child of CodeBlock, it floats over the content.
Also accepts the props of React.ComponentProps<"div">.
CodeBlockCopyButton
A ghost icon button that copies value and announces "Copied" in a polite live region.
| Prop | Type | Default | Description |
|---|---|---|---|
value | string | - | Required. The text to copy. |
Also accepts the props of Button.
CodeBlockContent
The pre and code pair that scrolls or wraps.
| Prop | Type | Default | Description |
|---|---|---|---|
children | React.ReactNode | - | A string, or a list of CodeBlockLine. |
wrap | boolean | false | Wraps long lines instead of scrolling. |
lineNumbers | boolean | false | Numbers each line. A string child is split into lines. |
startLine | number | 1 | The number of the first line when lineNumbers is set. |
tabIndex | number | 0 | Keeps the content focusable so it scrolls by keyboard. |
Also accepts the props of React.ComponentProps<"pre">.
CodeBlockLine
One line of code.
| Prop | Type | Default | Description |
|---|---|---|---|
variant | "default" | "added" | "removed" | "highlighted" | "default" | The state of the line. |
Also accepts the props of React.ComponentProps<"span">.
CodeBlockFooter
A row below the content, such as for a Show more button.
Also accepts the props of React.ComponentProps<"div">.
useHighlightedCode
Returns one React node per line. Until the first pass finishes, and for a language outside the list, it returns plain lines.
| Argument | Type | Description |
|---|---|---|
code | string | The code to highlight. A trailing newline is ignored. |
language | string | A language or alias from lib/highlighter.tsx, or "ansi". |
Accessibility
CodeBlockContentrenders aprewith acodeelement and always setsdir="ltr", so code reads left to right on a right-to-left page.- The content has
tabIndex={0}so keyboard users can focus and scroll it. PasstabIndex={-1}to remove the tab stop. - Floating actions appear when focus is inside the block, not only on hover.
- Line numbers are hidden from screen readers.
- Added and removed lines carry a
+or−marker as well as a tint, so the change does not rely on color. CodeBlockCopyButtonis labelled "Copy" and announces "Copied" after a successful copy.- Give a wrap toggle
aria-pressed, and a Show more buttonaria-expandedwitharia-controlspointing at the contentid.
Notes
- Styling hooks:
data-slot="code-block",code-block-header,code-block-title,code-block-actions,code-block-copy-button,code-block-content,code-block-code,code-block-lineandcode-block-footer. The root exposesdata-variantanddata-size, and each line exposesdata-variant.
A CodeBlock shows code in a rounded, muted surface. Put a CodeBlockTitle and CodeBlockActions with a CodeBlockCopyButton in a CodeBlockHeader, the code in CodeBlockContent, and secondary text in a CodeBlockFooter.
CodeBlockContent takes a string or CodeBlockLine children. Long lines scroll sideways unless you set wrap. Pass language to color keywords, strings, numbers and comments with a small built-in highlighter that uses the theme colors, so it follows light and dark mode and every style.
Installation
The CLI also adds what it builds on: Icon, Text.
Manual
-
Target iOS 17 or later.
-
Copy the theme files every view uses:
- Copy the source and every file it uses:
- Add the files to your app target.
- Set
minSdkto 24 or higher and add the dependencies:
- Copy the theme files every view uses:
- Copy the source and every file it uses:
- Update the
com.esuiss.uipackage names to match your project.
Usage
In SwiftUI and Compose, pass the code as a string or a list of CodeBlockLine values to CodeBlockContent. The copy button uses expo-clipboard in React Native, UIPasteboard in SwiftUI and the Compose clipboard on Android.
Examples
Basic
A single line with no header.
Line numbers
Numbers every line with lineNumbers.
Start line
Starts the numbering at startLine for an excerpt of a longer file.
Wrap
Wraps long lines instead of scrolling sideways.
Highlighted lines
CodeBlockLine with variant="highlighted" tints the lines that matter.
Diff
added and removed lines get a tint and a + or − marker.
Size
The sm size for code inside a message or a trace.
Destructive
The destructive variant for errors.
Languages
The built-in highlighter colors TypeScript, JSON, shell, Swift and Kotlin with the chart colors of the theme.
Footer
A CodeBlockFooter with secondary text below the code.
API Reference
CodeBlock
The surface that holds the header, code and footer.
| Prop | Type | Default | Description |
|---|---|---|---|
variant | "default" | "destructive" | "default" | Muted surface or a destructive tint for errors. |
size | "default" | "sm" | "default" | Text size, padding and corner radius. |
Also accepts the props of ViewProps.
CodeBlockHeader
A row above the code for an icon, a title and actions.
CodeBlockTitle
One line of text that truncates, usually a file name.
CodeBlockActions
Holds buttons at the end of the header.
CodeBlockCopyButton
Copies value and shows a check for two seconds.
| Prop | Type | Default | Description |
|---|---|---|---|
value | string | — | The text to copy. |
CodeBlockContent
The code, from a string or CodeBlockLine children.
| Prop | Type | Default | Description |
|---|---|---|---|
language | string | — | Colors the code: ts, tsx, js, json, bash, python, swift, kotlin and their aliases. |
wrap | boolean | false | Wraps long lines instead of scrolling sideways. |
lineNumbers | boolean | false | Shows a number before each line. |
startLine | number | 1 | The number of the first line. |
CodeBlockLine
One line of code.
| Prop | Type | Default | Description |
|---|---|---|---|
variant | "default" | "added" | "removed" | "highlighted" | "default" | Tints the line and adds a + or − marker for diffs. |
CodeBlockFooter
Secondary text below the code.
Accessibility
- The copy button has the label Copy, and Copied after a copy.
- Line numbers and diff markers are hidden from assistive technology.