Skip to content
UI

Code Block

beta

Shows code in a monospaced block, colored when the code is whole, with an optional header and copy button.

@zenoxai/ui/patterns/code-block Markdown
HTML
<!-- 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

  • wrap wraps long lines, breaking a long token anywhere. Without it the block scrolls sideways. Wrap in a narrow column such as a sheet.
  • showLineNumbers numbers 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.

python
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.

PropTypeDefault
code*string

The code, shown and copied exactly as given.

-
labelstring

Already-translated name of the block, announced while it scrolls and can take the keyboard focus.

Code
languagestring

The 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.

-
showLineNumbersboolean

Numbers the lines from 1 when there is more than one.

false
wrapboolean

Wraps 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.

PropTypeDefault
copiedLabelstring

Already-translated text announced to screen readers after a copy.

-
iconLucideIcon

Replaces the copy glyph, for example with a link glyph for "Copy link".

-
labelstring

Already-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.