Code Block
betaShows code in a monospaced block, colored when the code is whole, with an optional header and copy button.
@zenoxai/ui/patterns/code-block Markdown<!-- Acme tracking pixel -->
<script>
(function () {
var beacon = new Image();
beacon.src = "https://px.acme.com/b/7f3a9c?u=" + encodeURIComponent(location.href);
})();
</script>function CodeBlockDemo() {
const tag = `<!-- Acme tracking pixel -->
<script>
(function () {
var beacon = new Image();
beacon.src = "https://px.acme.com/b/7f3a9c?u=" + encodeURIComponent(location.href);
})();
</script>`
return (
<CodeBlock code={tag} language="html" wrap className="w-full max-w-xl">
<CodeBlockHeader>
<CodeBlockTitle>HTML</CodeBlockTitle>
<CodeBlockCopyButton label="Copy the tag" />
</CodeBlockHeader>
</CodeBlock>
)
}Usage
import {
CodeBlock,
CodeBlockCopyButton,
CodeBlockHeader,
CodeBlockTitle,
} from "@zenoxai/ui/patterns/code-block"
<CodeBlock code={installation.snippet} language="html" wrap>
<CodeBlockHeader>
<CodeBlockTitle>HTML</CodeBlockTitle>
<CodeBlockCopyButton label={t("ui.actions.copy")} />
</CodeBlockHeader>
</CodeBlock>
Use a Code Block for code people read or paste: a tag to install, a JSON payload, a rule, a
fenced block from the assistant, an excerpt of a leaked file. For one value in running text, use
<code>; for prose, the screen’s long-text rendering.
The colors come from Shiki, an optional peer dependency. Install it in the app that renders code blocks:
pnpm add shiki @shikijs/langs
Import the block from @zenoxai/ui/patterns/code-block; the root entry does not export it. Shiki
loads on the first block that has a language, and each grammar on first use, so a page without
colored code downloads none of it.
Pass language only for code that is whole
A highlighter reads the code as a whole file. An excerpt cut out of one can start inside a string, a comment or an object, and its colors come out wrong: in JSON, keys take the color of values; in Python, the code after a docstring reads as a string. A wrong color reads as a claim about the code, so an excerpt stays plain.
| Code | language |
|---|---|
| A tag to paste, a JSON payload, a fenced block | yes, once the message is complete |
| Lines cut out of a file, a paste window, a page source | no |
The block colors json, yaml, html, xml, css, javascript, typescript, jsx, tsx, python,
shellscript, powershell, sql, go, java, php, dotenv, ini and toml, and Shiki’s aliases of them
(js, ts, py, sh, bash, yml). Any other value stays plain, as does code above 20,000
characters; a line of 1,000 characters or more is left uncolored.
The colors are the --code-* tokens (Colors): keywords and tag
names, strings, comments, and the text color for the rest. No red, orange or green, which carry
severity and status.
Layout
wrapwraps long lines, breaking a long token anywhere. Without it the block scrolls sideways. Wrap in a narrow column such as a sheet.showLineNumbersnumbers the lines from 1 when there is more than one. A wrapped line has no number on its continuation.- The block is capped at 18rem and scrolls beyond it. A list of blocks, and its “Show all”, belong to the screen.
Examples
An excerpt from a file
Lines cut out of a leaked file stay plain, numbered and wrapped, with the copy button floating at the top right. It shows on hover and on focus.
"default": {
"ENGINE": "django.db.backends.postgresql",
"HOST": "db.acme.com",
"USER": "svc_billing",
"PASSWORD": "Acme@2026!", # TODO move to vault
"PORT": 5432,
}function CodeBlockExcerpt() {
// Lines cut out of a leaked file: whole lines, but not a whole file, so
// the block takes no language and stays plain.
const excerpt = ` "default": {
"ENGINE": "django.db.backends.postgresql",
"HOST": "db.acme.com",
"USER": "svc_billing",
"PASSWORD": "Acme@2026!", # TODO move to vault
"PORT": 5432,
}`
return (
<CodeBlock
code={excerpt}
showLineNumbers
wrap
label="Excerpt from billing/settings.py"
className="w-full max-w-xl"
>
<CodeBlockCopyButton label="Copy the excerpt" />
</CodeBlock>
)
}A payload
A JSON payload the app serialized itself is whole, so it is colored. It scrolls sideways without
wrap. No header and no copy button when the screen has no use for them.
{
"cveId": "CVE-2025-39946",
"state": "PUBLISHED",
"metrics": {
"cvssV3_1": {
"baseScore": 7.8,
"baseSeverity": "HIGH"
}
},
"kev": false,
"references": [
"https://git.kernel.org/stable/c/1a2b3c"
]
}function CodeBlockPayload() {
const payload = JSON.stringify(
{
cveId: "CVE-2025-39946",
state: "PUBLISHED",
metrics: { cvssV3_1: { baseScore: 7.8, baseSeverity: "HIGH" } },
kev: false,
references: ["https://git.kernel.org/stable/c/1a2b3c"],
},
null,
2
)
return (
<CodeBlock
code={payload}
language="json"
label="Raw payload"
className="w-full max-w-xl"
/>
)
}A fenced block from the assistant
A header names the language and holds the copy button. Pass language once the message has
finished streaming; until then the block stays plain.
import requests
# Look up the domain in the registry
resp = requests.get("https://rdap.org/domain/acme.com", timeout=10)
print(resp.json()["status"])function CodeBlockFenced() {
const code = `import requests
# Look up the domain in the registry
resp = requests.get("https://rdap.org/domain/acme.com", timeout=10)
print(resp.json()["status"])`
return (
<CodeBlock code={code} language="python" className="w-full max-w-xl">
<CodeBlockHeader>
<CodeBlockTitle>python</CodeBlockTitle>
<CodeBlockCopyButton />
</CodeBlockHeader>
</CodeBlock>
)
}Accessibility
- The code is real text, line by line, in reading order. Line numbers are hidden from screen readers and from text selection.
- While the block scrolls, it is a region named by
label(“Code” by default) and takes the keyboard focus, so it can be scrolled with the arrow keys. Name it after its content. - Color is never the only signal: quotes, comment markers and keywords are in the text.
- The copy button is named by its
label(“Copy” by default) and copies the code exactly as given.
API reference
Generated from the TypeScript source. className, style and children are accepted everywhere and omitted below.
CodeBlock
CodeBlock shows code in a monospaced block: a file, a payload, a tag to paste, a leaked excerpt. With `language` it colors code that is whole, once the grammar loads; until then, and without a language, the code is plain. Long code scrolls inside an 18rem cap. Put a CodeBlockHeader or a CodeBlockCopyButton inside it.
| Prop | Type | Default |
|---|---|---|
code* | stringThe code, shown and copied exactly as given. | - |
label | stringAlready-translated name of the block, announced while it scrolls and can take the keyboard focus. | Code |
language | stringThe language to color the code in: json, yaml, html, xml, css, javascript, typescript, jsx, tsx, python, shellscript, powershell, sql, go, java, php, dotenv, ini or toml, or a Shiki alias of one (`js`, `py`, `sh`, `yml`). Pass it only for code that is whole. An excerpt cut out of a file colors wrong, so leave it out and the block stays plain; any other value stays plain too. | - |
showLineNumbers | booleanNumbers the lines from 1 when there is more than one. | false |
wrap | booleanWraps long lines instead of scrolling sideways. | false |
CodeBlockCopyButton
CodeBlockCopyButton copies the block's code exactly as given. In a CodeBlockHeader it sits on the right; as a direct child of CodeBlock it floats at the top right and shows on hover and focus.
| Prop | Type | Default |
|---|---|---|
copiedLabel | stringAlready-translated text announced to screen readers after a copy. | - |
icon | LucideIconReplaces the copy glyph, for example with a link glyph for "Copy link". | - |
label | stringAlready-translated accessible name and tooltip. | - |
onCopy | ((value: string) => void)Called after the value reached the clipboard. Show a toast here when the surface needs more feedback than the check icon. | - |
onCopyError | ((error: unknown) => void)Called when the value could not be written: the page is not a secure context (no Clipboard API) or the browser denied the write. | - |
CodeBlockHeader
CodeBlockHeader is the band above the code: a CodeBlockTitle on the left (the language, a file name) and a CodeBlockCopyButton on the right. Use it where the block needs a name; otherwise float the copy button.
No props of its own. Accepts the props of the element it renders.
CodeBlockTitle
CodeBlockTitle names the code in a CodeBlockHeader: "HTML", "python".
No props of its own. Accepts the props of the element it renders.
highlightCode
highlightCode colors code with the Zenox theme. It resolves to one array of tokens per line, or to null when the code stays plain: a language without a grammar here, code above the size limit, or a grammar or chunk that failed, which is then not tried again.
No props of its own. Accepts the props of the element it renders.
MAX_LENGTH
Above this many characters a block stays plain.
No props of its own. Accepts the props of the element it renders.
resolveLanguage
The grammar for a language name or alias, or undefined when it has none.
No props of its own. Accepts the props of the element it renders.