CopyButton
A copy-to-clipboard button that confirms the copy: the icon becomes a check and the button shows the success colour for a moment. Icon-only by default; pass label for a labelled variant.
Playground
<CopyButton
intent="neutral"
label=""
value="npm i @urbicon-ui/blocks"
variant="ghost"
/>01 Examples
Icon-only (default)
npm i @urbicon-ui/blocks <code>npm i @urbicon-ui/blocks</code>
<CopyButton value="npm i @urbicon-ui/blocks" />With a label
<CopyButton value="npm i @urbicon-ui/blocks" label="Copy" variant="outlined" />Variants
{#each ['ghost', 'outlined', 'filled'] as variant}
<CopyButton value="urbicon" label="Copy" {variant} />
{/each}02 In context
Copy an install command
npm i @urbicon-ui/blocks <Card padding="lg">
<div class="flex items-center justify-between gap-4">
<code>npm i @urbicon-ui/blocks</code>
<CopyButton value="npm i @urbicon-ui/blocks" />
</div>
</Card>onCopy callback
<script>
let copies = $state(0);
</script>
<CopyButton value="urbicon" label="Copy token" onCopy={() => copies++} />
<span>Copied {copies} time(s)</span>03 Accessibility
Stable name in icon-only mode
In icon-only mode the accessible name stays fixed (Copy, or your own aria-label); the copied/failed outcome is announced by the live region below,
not by renaming the button.
Label in Name in labelled mode
In labelled mode the visible text is the accessible name (WCAG 2.5.3, Label in Name); no aria-label overrides it.
The result is announced
A visually-hidden role="status" live region announces the result (Copied or Copy failed) the moment it happens, in both modes and without moving focus. An aria-label change alone is not reliably announced, so the icon-only default would
otherwise be silent to a screen reader.
The icon is decorative
The copy/check icon is aria-hidden; sighted users read state from the icon, the
intent colour, and (in labelled mode) the swapped label.
Failure has a path
The clipboard write can fail in an insecure (non-HTTPS) context or when permission is
denied. That path calls onError and shows the failed state rather than throwing through
the render.
04 API Reference
19 propsProp | Type | Default | Description | |
|---|---|---|---|---|
value required | string | — | The text written to the clipboard when pressed. | |
children | Snippet<[CopyPhase]> | — | Button content, like any other Button — an icon plus text, a Kbd, whatever
the action needs. Wins over label, which stays as the string shorthand for
the common case. Receives the copy phase, so the content can answer the copy
itself; ignoring the argument keeps the content static while the icon and
intent still flip. | |
class | string | — | Additional CSS class merged onto the underlying Button. | |
copiedLabel | string | the i18n "Copied" string | Visible label shown for timeout ms after a successful copy. | |
disabled inherited | boolean | false | Whether the button is disabled | |
hideIcon | boolean | false | Hide the copy/check icon (label-only). | |
intent inherited | primarysecondarysuccesswarning +2 more | — | Inherited from ButtonProps. | |
label | string | — | Optional visible label next to the icon. When omitted the button is icon-only. | |
onCopy | (value: string) => void | — | Called with value after it is written to the clipboard. | |
onError | (error: unknown) => void | — | Called with the thrown error when the clipboard write fails (permission denied, insecure context, …). | |
preset | string | — | Apply a named preset registered via <BlocksProvider presets={{ CopyButton: {...} }}>.
Prefer this over class overrides when the requested look falls outside the
semantic intent palette — presets keep hover/active/dark-mode logic coherent. | |
size variant | 2xslgmdsm +2 more | md | Controls the dimensions, padding, and text size of the CopyButton. Affects the component's physical footprint. Available options: 2xs, lg, md, and 3 more. | |
slotClasses | Partial<Record<CopyButtonSlots, string>> | — | Per-slot class overrides. Slots: base | icon | label | |
state variant | copiederroridle | idle | Controls the visual state representation of the CopyButton to communicate status to users. Available options: copied, error, idle. | |
tier inherited | commitmodify | — | Inherited from ButtonProps. | |
timeout | number | 2000 | How long the success/error state stays before reverting, in ms. 0 keeps it until the next copy. | |
unstyled | boolean | — | Strip all default styles; forwarded to the underlying Button. | |
variant inherited | filledoutlinedghosttext | — | Inherited from ButtonProps. | |
...HTMLButtonAttributes inherited | HTMLAttributes | — | HTML attributes (excluding: 'value' | 'onclick' | 'class' | 'disabled' | 'children') |
05 Types
Local type definitions used by this component.
Name | Kind | Category | Used by | Description | |
|---|---|---|---|---|---|
CopyButtonProps | interface | props | 0 | — | |
CopyPhase | type | helper | 0 | The button's copy phase: 'idle' before a copy, 'copied' after a successful
write, 'error' on failure. 'error' reverts on the same timer as 'copied'. | |
ButtonProps | interface | props | 0 | Props interface for Button component | |
CopyButtonSlots | type | variant | 0 | Slot names derived from the tv() config above — single source of truth for slotClasses. | |
ButtonSlots | type | variant | 0 | Slot names derived from the tv() config above — single source of truth for slotClasses. | |
MintProp | type | helper | 0 | — | |
SlotNames | type | helper | 0 | Extracts the slot-name union from a slotted tv() config function — the
companion to VariantProps. The slot-mode overload returns
(props?) => { [K in keyof S]: SlotFn }, so keyof ReturnType<T> is exactly
the set of slot names a component declares in tv({ slots: … }).
Use it to type a component's slotClasses prop from the single source of
truth (its *.variants.ts) instead of hand-maintaining a parallel union
that silently drifts when a slot is added or renamed: | |
MintName | type | helper | 0 | A mint name: a built-in (autocompleted), 'none' to disable, or any
consumer-registered name. (string & {}) keeps the registry open — a
custom name still type-checks, it just isn't suggested. A typo therefore
also still compiles (it resolves like an unregistered custom name and
warns at runtime); the union buys completion and docs, not validation. | |
MintConfig | interface | helper | 0 | — |
06 Installation
Import
import { CopyButton } from '@urbicon-ui/blocks';