# Code Block

A code sample or a command in the monospace font, with a copy button.

```tsx
import { Card, CardContent, CardDescription, CardHeader, CardTitle, CodeBlock } from 'ferry-ui'

const SAMPLE = `import { Acme } from '@acme/sdk'

const acme = new Acme(process.env.ACME_API_KEY)
await acme.invoices.create({ customer: 'cus_4QbX2', amount: 4280 })`

export default function CodeBlockHero() {
  return (
    <Card className="w-full max-w-lg">
      <CardHeader>
        <div>
          <CardTitle>Install the SDK</CardTitle>
          <CardDescription>Send your first request to the Invoices API.</CardDescription>
        </div>
      </CardHeader>
      <CardContent className="flex flex-col gap-4">
        <div className="flex flex-col gap-1.5">
          <p className="text-[13px] font-medium text-foreground">Add the package</p>
          <CodeBlock prompt code="npm install @acme/sdk" />
        </div>
        <div className="flex flex-col gap-1.5">
          <p className="text-[13px] font-medium text-foreground">Create an invoice</p>
          <CodeBlock what="code sample" code={SAMPLE} />
        </div>
      </CardContent>
    </Card>
  )
}
```

## Usage guidelines

- **Text that the user copies.** Use it for a command, a config sample or an API example.
- **One value in a form is a field.** Use `CopyField`. For a secret that the user can show, use `SecretField`. See [Copy](/docs/components/copy).
- **Not an editor.** For code that the user edits, use a [Textarea](/docs/components/textarea) with `mono`.
- **No syntax colors.** The block shows the text as you give it.

## Anatomy

Import the parts. Give plain text to `code`. For content with elements, use the children and `copyValue`.

```tsx title="Anatomy"

<CodeBlock code="" />

<CodeBlock copyValue="">
  <CodeBlockPrompt />
</CodeBlock>
```

The `variant` prop sets the look.

| Variant | Look | Use |
| --- | --- | --- |
| `block` (default) | A box with a border. | Commands and code samples. |
| `inline` | A compact chip of one line. | A short token in a list or in help text. |
| `terminal` | A decorative window. | An illustration in an empty state. |

## Examples

### Prompt

`prompt` shows a `$` before each line of `code` that is not empty. The copy button does not copy the prompt. Pass a string to show a different prompt.

```tsx
import { CodeBlock } from 'ferry-ui'

const SETUP = `git clone https://example.com/acme/web-app.git
cd web-app

npm install
npm run dev`

export default function CodeBlockPromptProp() {
  return (
    <div className="flex w-full max-w-md flex-col gap-3">
      <CodeBlock prompt code="npx acme login" />
      <CodeBlock prompt code={SETUP} what="setup commands" />
      <CodeBlock prompt="sql>" code="SELECT id, total FROM invoices;" what="query" />
    </div>
  )
}
```

### Masked value

`copyValue` sets the text that the button copies. Use it to show a masked key and to copy the real key.

```tsx
import { CodeBlock } from 'ferry-ui'

export default function CodeBlockMasked() {
  return (
    <CodeBlock
      className="max-w-md"
      prompt
      code="export ACME_API_KEY=sk_demo_••••3f6a"
      copyValue="export ACME_API_KEY=sk_demo_4f9a2c7e1b8d3f6a"
      what="command with your API key"
    />
  )
}
```

### Long lines

A long line scrolls in the block. `copyPlacement="side"` puts the copy button in its own column. `wrap` breaks the long lines.

```tsx
import { CodeBlock } from 'ferry-ui'

const REQUEST =
  'curl -X POST https://api.example.com/v2/invoices -H "Authorization: Bearer $ACME_API_KEY" -d customer=cus_4QbX2'

export default function CodeBlockLongLines() {
  return (
    <div className="flex w-full max-w-sm flex-col gap-3">
      <CodeBlock code={REQUEST} what="request" />
      <CodeBlock code={REQUEST} what="request" copyPlacement="side" />
      <CodeBlock code={REQUEST} what="request" wrap />
    </div>
  )
}
```

### Inline

The `inline` variant is a chip of one line. Use it for a short token in a list or in help text.

```tsx
import { CodeBlock } from 'ferry-ui'

const PLACEHOLDERS = [
  { code: '{{customer.first_name}}', help: 'The first name of the customer.' },
  { code: '{{invoice.total}}', help: 'The total of the invoice, with its currency.' },
  { code: '{{workspace.portal_url}}', help: 'The link to the billing portal of the workspace.' },
]

export default function CodeBlockInline() {
  return (
    <div className="w-full max-w-sm divide-y rounded-lg border">
      {PLACEHOLDERS.map((placeholder) => (
        <div key={placeholder.code} className="flex flex-col gap-1.5 px-4 py-3">
          <CodeBlock variant="inline" code={placeholder.code} what="placeholder" />
          <p className="text-[13px] text-foreground-light">{placeholder.help}</p>
        </div>
      ))}
    </div>
  )
}
```

### Terminal

The `terminal` variant is decorative. It has no copy button unless you set `copyable`. If it repeats the text near it, add `aria-hidden`.

```tsx
import { CodeBlock, CodeBlockPrompt } from 'ferry-ui'

export default function CodeBlockTerminal() {
  return (
    <CodeBlock variant="terminal" className="w-full max-w-md">
      <CodeBlockPrompt />
      npm run test
      {'\n'}
      <span className="text-success">✓ invoices.test.ts (12 tests)</span>
      {'\n'}
      <span className="text-success">✓ customers.test.ts (8 tests)</span>
      {'\n'}
      <span className="text-warning">! 1 snapshot is obsolete</span>
    </CodeBlock>
  )
}
```

### Rich content

The children replace `code`. Use them to give a color to a part of the text. `CodeBlockPrompt` adds the prompt. The copy button shows only if you pass `copyValue`.

```tsx
import { CodeBlock, CodeBlockPrompt } from 'ferry-ui'

export default function CodeBlockRich() {
  return (
    <CodeBlock className="max-w-md" copyValue="acme invoices list --status overdue" what="command">
      <CodeBlockPrompt />
      acme invoices list <span className="text-primary">--status</span> overdue
    </CodeBlock>
  )
}
```

### No copy button

`copyable={false}` hides the copy button. Use it for a read-only sample that the user must not paste as it is.

```tsx
import { CodeBlock } from 'ferry-ui'

const OUTPUT = `Invoice INV-2041 has the status "paid".
Amount: $4,280.00`

export default function CodeBlockNotCopyable() {
  return <CodeBlock className="max-w-md" code={OUTPUT} copyable={false} />
}
```

## Accessibility

- `what` names the content in the name of the copy button, such as "Copy API key".
- The default of `what` is "command" for a block with `prompt`. If not, it is "code".
- The copy button needs the `TooltipProvider` and the `Toaster` of the app. See [Quick start](/docs/overview/quick-start).

## API reference

`CodeBlock` also accepts each attribute of the `<div>` element. `CodeBlockPrompt` accepts each attribute of the `<span>` element.

### CodeBlock

The [Copy](/docs/components/copy) page gives the keys of `labels`.

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `code` | `string` |  | The snippet as plain text, shown verbatim (whitespace and line breaks preserved). It is also what the copy button copies unless `copyValue` is set. |
| `children` | `ReactNode` |  | Rich content shown instead of `code`, e.g. tinted output lines (`<span className="text-success">✓ 12 passed</span>`) or a `CodeBlockPrompt`. When only `children` are given, pass `copyValue` or no copy button is rendered. |
| `copyValue` | `string` |  | What the copy button writes to the clipboard. Defaults to `code`. Use it when the displayed text differs from the real value, e.g. show `sk_demo_••••3f6a` but copy the full key. |
| `copyable` | `boolean` |  | Show a copy button. Default `true`, except for the `terminal` variant (decorative) where it defaults to `false`. The button only appears when there is something to copy. |
| `what` | `string` |  | What is being copied, for the copy button's accessible name and tooltip ("API key" → "Copy API key"). Defaults to "command" when `prompt` is set, "code" otherwise. |
| `prompt` | `string \| boolean` | `false` | Shell prompt shown before every non-empty line of `code` (muted, not selectable, never copied), so each line reads as its own command: do not use it for a command continued over several lines with a trailing `\`. `true` renders `$`; pass a string for another prompt (`>`, `PS>`). Default: no prompt. Ignored with `children`: put a `CodeBlockPrompt` in them instead. |
| `size` | `"sm" \| "md"` |  | Text size: `sm` 12px (popovers, dense panels) or `md` 12.5px. Default `md` (`sm` for `inline`). |
| `wrap` | `boolean` | `false` | Wrap long lines (breaking anywhere) instead of scrolling horizontally. Use it in narrow containers where a horizontal scrollbar would hide the end of the line. Default `false`. The `inline` variant always wraps, so it ignores this prop. |
| `variant` | `"block" \| "inline" \| "terminal"` | `block` | - `block` (default): bordered sunken box for commands and snippets. - `inline`: compact one-line chip with a ghost copy button, for a short token or syntax example inside a list or help text. - `terminal`: decorative window with three dots and a card shadow, for marketing panels, empty states and onboarding illustrations. Not copyable unless `copyable` is set; add `aria-hidden` when it only illustrates what the surrounding text already says. |
| `copyPlacement` | `"side" \| "overlay"` | `overlay` | Where the copy button of a `block` sits (ignored by `inline` and `terminal`): - `overlay` (default): floating in the top-right corner over the snippet. - `side`: in its own column, so a long line never scrolls under the button. Prefer it for long single-line commands. |
| `onCopy` | `((value: string) => void)` |  | Called with the copied value each time the user copies it (analytics). |
| `labels` | `Partial<CopyLabels>` |  | Overrides of the copy button's built-in texts ("Copy <what>", "Copied"), to translate or reword them. See `CopyLabels`; unset entries keep their English default. |

### CodeBlockPrompt

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `children` | `ReactNode` | `$` | Prompt text, `$` by default (`>`, `PS>`, `sql>`). A space is added after it. |
