# Code Block

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

```tsx
import { CodeBlock, CodeBlockCopyButton, CodeBlockHeader, CodeBlockTitle } from "@zenoxai/ui/patterns/code-block"

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

```tsx
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](https://shiki.style), an optional peer dependency. Install it in
the app that renders code blocks:

```bash
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](/foundations/colors#code)): 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.

```tsx
import { CodeBlock, CodeBlockCopyButton } from "@zenoxai/ui/patterns/code-block"

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.

```tsx
import { CodeBlock } from "@zenoxai/ui/patterns/code-block"

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.

```tsx
import { CodeBlock, CodeBlockCopyButton, CodeBlockHeader, CodeBlockTitle } from "@zenoxai/ui/patterns/code-block"

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

`import { ... } from "@zenoxai/ui/patterns/code-block"`

### CodeBlock

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| code (required) | `string` |  | The code, shown and copied exactly as given. |
| label | `string` | `Code` | Already-translated name of the block, announced while it scrolls and can take the keyboard focus. |
| language | `string` |  | 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. |
| showLineNumbers | `boolean` | `false` | Numbers the lines from 1 when there is more than one. |
| wrap | `boolean` | `false` | Wraps long lines instead of scrolling sideways. |

### CodeBlockCopyButton

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| copiedLabel | `string` |  | Already-translated text announced to screen readers after a copy. |
| icon | `LucideIcon` |  | Replaces the copy glyph, for example with a link glyph for "Copy link". |
| label | `string` |  | 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

No props of its own.

### CodeBlockTitle

No props of its own.

### highlightCode

No props of its own.

### MAX_LENGTH

No props of its own.

### resolveLanguage

No props of its own.

