CodeBlockexperimental
Read-only code display card with a copy button, an accessible copy status, and horizontal scroll kept inside the block. It renders raw text, so syntax highlighting comes from a consumer or the StreamingMarkdown renderer.
Playground
type Source = { id: string; title: string; url?: string };
function cite(sources: Source[]): string {
return sources.map((s, i) => `[${i + 1}] ${s.title}`).join('\n');
} <CodeBlock lang="ts" {code} />01 Examples
Multi-line code
import { CodeBlock } from '@urbicon-ui/blocks';
export function greet(name: string): string {
return `Hello, ${name} — this line is long enough to scroll inside the block.`;
} <script lang="ts">
import { CodeBlock } from '@urbicon-ui/blocks';
// Rendered as raw text — CodeBlock never highlights on its own. Long lines
// scroll horizontally inside the block, so the page never scrolls sideways.
const code = `import { CodeBlock } from '@urbicon-ui/blocks';
export function greet(name: string): string {
return \`Hello, \${name} — this line is long enough to scroll inside the block.\`;
}`;
</script>
<CodeBlock lang="ts" {code} />
Track copies with onCopy
curl -fsSL https://ui.urbicon.de/install.sh | sh Copied 0 times.
<script lang="ts">
import { CodeBlock } from '@urbicon-ui/blocks';
// onCopy fires only after the clipboard write succeeds — the perfect hook for
// analytics ("install command copied") without polling the clipboard.
let copies = $state(0);
const code = 'curl -fsSL https://ui.urbicon.de/install.sh | sh';
</script>
<div class="w-full space-y-2">
<CodeBlock lang="bash" {code} onCopy={() => (copies += 1)} />
<p class="text-text-tertiary text-xs">
Copied {copies} time{copies === 1 ? '' : 's'}.
</p>
</div>
Extra header actions
SELECT id, title FROM sources WHERE cited = true; <script lang="ts">
import { CodeBlock, Button } from '@urbicon-ui/blocks';
// The `actions` snippet renders extra controls in the header, before the copy
// button — e.g. "Run in playground", "Open in editor".
const code = 'SELECT id, title FROM sources WHERE cited = true;';
</script>
<CodeBlock lang="sql" {code}>
{#snippet actions()}
<Button variant="ghost" size="sm">Run</Button>
{/snippet}
</CodeBlock>
02 Accessibility
Copy button label
The copy button carries an aria-label that swaps from copyLabel ("Copy") to copiedLabel ("Copied") for two seconds after a successful
copy, so its accessible name always matches what the user sees. A denied or failed clipboard write
leaves the label unchanged, so a copy is confirmed only when it actually happened.
Status announcement
A visually hidden role="status" region announces "Copied"
to screen readers. It ships in the DOM up front and only its text content changes, which assistive
tech announces reliably.
Scrollable region
The code body is a focusable role="region" (tabindex="0") labelled by the language, so keyboard users can reach and scroll horizontally overflowing
code (WCAG 2.1.1). Focus rings use focus-visible: for keyboard-only visibility.
03 API Reference
18 propsProp | Type | Default | Description | |
|---|---|---|---|---|
code required | string | — | The code to display and copy. Rendered as raw text (no highlighting). | |
actions | Snippet | — | Extra header actions rendered in the header, just before the copy button. | |
class | string | — | Extra classes merged onto the root element. | |
copiedLabel | string | 'Copied' | Label shown for two seconds after a successful copy. | |
copyFailedLabel | string | 'Copy failed' | Label shown for two seconds after a FAILED copy — e.g. a denied clipboard permission or a non-secure context. | |
copyLabel | string | 'Copy' | Accessible label / tooltip for the copy button at rest. These labels are plain English defaults rather than i18n-resolved, so this leaf stays out of the i18n registry. Pass your own strings to localise. | |
label | string | — | Header caption, shown instead of lang. For an embedded block, naming what
the payload is ("Input", "Response body") is often clearer than the
language it is serialised in. lang still names the scrollable region for
screen readers when both are given. | |
lang | string | — | Language label shown in the header. Display-only — does not drive highlighting. | |
onCopy | (code: string) => void | — | Called with the copied code after a successful clipboard write. | |
onCopyError | (error: unknown) => void | — | Called with the thrown reason when the clipboard write fails. Without a handler the failure is logged; either way the button reports it. | |
preset | string | — | Apply a named preset registered via <BlocksProvider presets={{ CodeBlock: {...} }}>.
Prefer this over class overrides when the requested look falls outside the
semantic intent palette. | |
showCopy | boolean | — | Show the copy button in the header. | |
slotClasses | Partial<Record<CodeBlockSlots, string>> | — | Per-slot class overrides. Slots: root | header | langLabel | copyButton | pre | code.
Note variant="plain" deliberately leaves root/header/pre without surface,
outline or padding — the embedding parent supplies those. | |
unstyled | boolean | — | Remove all default tv classes. | |
variant variant | cardplain | card | Controls the visual style and presentation of the CodeBlock. Determines the component's visual treatment. Available options: card, plain. | |
wrap | boolean | — | Soft-wrap long lines. false (default) scrolls horizontally inside the
block; true wraps with whitespace-pre-wrap + word breaking. | |
...CodeBlockVariants variant | VariantProps | — | Styling variants from CodeBlockVariants | |
...HTMLAttributes<HTMLElement> inherited | HTMLAttributes | — | HTML attributes (excluding: 'children' | 'class') |
04 Types
Name | Kind | Category | Used by | Description | |
|---|---|---|---|---|---|
CodeBlockProps | interface | props | — | — | |
CodeBlockVariants | type | variant | 1 | — |
05 Installation
Import
import { CodeBlock } from '@urbicon-ui/blocks';
import type { CodeBlockProps } from '@urbicon-ui/blocks';