CodeBlock
Text meant to be read exactly.
Why it exists
Nineteen files render a <pre> and eleven of them JSON.stringify into one, each with its own padding, background and decision about overflow. That last one is the bug: several set no overflow at all, so a single long line — a base64 body, a minified error — stretched the page and gave the whole document a horizontal scrollbar. On a phone that makes every other element scroll sideways too.
POST /v1/messages 400 {"error":"invalid_recipient","detail":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkF2ZXJvYSIsImlhdCI6MTUxNjIzOTAyMn0.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c"}A payload
JsonBlock stringifies for you, because the failure mode is shared: JSON.stringify throws on a circular structure and returns undefined for a function, and a page that renders a payload has no business crashing over the shape of one.
{
"id": "evt_9f2c4a7e",
"type": "message.delivered",
"recipient": "ops@example.co.id",
"provider": {
"name": "postmark",
"latencyMs": 412,
"region": "eu-west-1"
},
"attempts": [
{
"at": "2026-09-08T11:58:02Z",
"status": "failed"
}
]
}Numbered, for a trace
Line numbers where the line numbers are the point. They are select-none, so copying the block by hand does not drag them into the paste — and the copy button never had them in the first place.
TypeError: Cannot read properties of undefined (reading 'id') at resolveRecipient (/app/src/lib/echo/send.ts:142:28) at async deliver (/app/src/lib/echo/deliver.ts:61:14) at async tick (/app/src/app/api/cron/tick/route.ts:29:5)In both themes
light
curl -X POST https://echo.trellis.id/v1/messages \
-H "authorization: Bearer $ECHO_KEY"dark
curl -X POST https://echo.trellis.id/v1/messages \
-H "authorization: Bearer $ECHO_KEY"Guidance
Use it when
- A payload, a command, a trace — anything a person will read character by character or paste elsewhere.
- maxLines whenever the content is unbounded. A 4,000-line payload buries the page it was attached to.
- numbered for a trace, where the line number is how people talk about it.
Reach for something else when
- Prose. Monospace tells the reader this is an identifier, and a sentence in it is a mixed signal.
- A single short value with a copy button — that is CopyField, which wraps rather than scrolls.
- Syntax highlighting. It is not here, deliberately: it needs a parser per language, and a wrong highlight is a false claim about the code.
Props
| Prop | Type | Default | Description |
|---|---|---|---|
| code | string | — | The text. |
| title | string | — | A filename, an endpoint, a content type — shown in the bar above. |
| copy | boolean | true | The copy button. This text exists to be pasted. |
| maxLines | number | — | Caps the height. The block still scrolls to the end. |
| numbered | boolean | false | Line numbers down the left. |
| JsonBlock value | unknown | — | Stringified for you, and safely. |
Accessibility
The <pre> carries tabIndex=0, because a region that scrolls must be reachable by keyboard — without it, everything past the right edge is available only to a mouse. The focus ring is inset so it draws inside the block rather than overlapping the border above it.