Clipboard
Copy-to-clipboard building blocks on Ark UI's Clipboard machine — the one-line Copy icon button, and the full Clipboard compound for a read-only "value + copy button" field.
Two ways to copy a value to the clipboard, both on top of Ark UI's Clipboard machine:
Copy— a one-line icon button. No visible value, just anActionIconthat swaps to a checkmark on copy. The common case (a share link's copy button, an API key's copy affordance next to other text).Clipboard— the full compound (Root/Label/Control/Input/Trigger/Indicator/ValueText), for a read-only field that shows the value and a copy button.
import { Copy } from '@42-central/ui-react/clipboard';
<Copy value="https://example.com/s/abc123" />;Playground
Component
Dir
Presets
Props
Code
<Component value="https://example.com/s/abc123" timeout={3000} />Basic usage
value is the only required prop. The icon swaps to a checkmark on copy and reverts to the copy icon after timeout.
Code
<Copy value="https://example.com/s/abc123" />Variants, sizes, colors & radius
Copy forwards variant / size / radius / color straight to ActionIcon — see its own docs for the full (variant × color) model. There's no separate styling surface to learn.
Code
<Copy value="…" variant="filled" color="brand" />
<Copy value="…" variant="light" color="teal" />
<Copy value="…" variant="outline" color="blue" />
<Copy value="…" variant="subtle" color="neutral" />
<Copy value="…" variant="filled" color="brand" radius="full" size="lg" />Timeout
timeout controls how long (in ms) the checkmark stays before reverting to the copy icon. Ark's own default is 3000.
Code
<Copy value="…" timeout={1000} />onCopiedChange
Fires true once the value is copied — flattened from Ark's own onStatusChange. It only fires on copy, never on the automatic revert to idle after timeout (that's Ark's own machine behavior, not a gap in this wrapper) — read the live state off Clipboard.Indicator if you need it.
<Copy
value="https://example.com/s/abc123"
onCopiedChange={(copied) => copied && track('share_link_copied')}
/>By default Copy is a closed leaf preset: it owns its ActionIcon outright and doesn't accept asChild or a custom aria-label — the accessible name comes from Ark's own "Copy to clipboard" / "Copied to clipboard" translations. For a different trigger element entirely, reach for the headless mode below, or compose Clipboard.Root / Clipboard.Trigger directly (further down) for translated text on the default element shape.
Headless
Pass a function as children instead of omitting it to go fully headless: Copy renders none of its own markup — no ActionIcon, no icon swap — and instead calls the function with { copied, copy }, so you render whatever trigger you want and wire copy to its onClick yourself. copied and copy still ride the same timeout / onCopiedChange behavior as the default button.
Code
<Copy value="https://example.com/s/abc123">
{({ copied, copy }) => (
<button type="button" onClick={copy}>
{copied ? 'Copied!' : 'Copy link'}
</button>
)}
</Copy>Copy doesn't attach a click handler to your returned JSX on its own — copy is only wired up where you put it, so a non-interactive render (a <span>, plain text) stays non-interactive. variant / size / radius / color / testId only apply to the default ActionIcon rendering — in headless mode they're unused, since there's no ActionIcon to forward them to.
The Clipboard field
For a read-only "value + copy button" field — share links, API keys, invite codes — compose the parts directly. Clipboard.Input renders the value in the shared InputBase shell (always read-only); the Trigger sits inside it as a real, focusable sibling — not through the shell's decorative endSlot, which would hide an interactive button from assistive tech.
Code
<Clipboard.Root defaultValue="https://example.com/s/abc123">
<Clipboard.Label>Share link</Clipboard.Label>
<Clipboard.Input>
<Clipboard.Trigger asChild>
<ActionIcon variant="subtle" color="neutral" size="sm" aria-label="Copy link">
<Clipboard.Indicator copied={<CheckIcon />}>
<CopyIcon />
</Clipboard.Indicator>
</ActionIcon>
</Clipboard.Trigger>
</Clipboard.Input>
</Clipboard.Root>Clipboard is parts-only — there's no bare-invocation convenience form, the same shape as Popover. A rich field like this isn't a single-prop drop-in the way Copy is.
Controlled value
value / onChange (flattened from Ark's onValueChange) make it controlled, same as any other value-bearing kit component.
Code
const [value, setValue] = useState('https://example.com/s/abc123');
<Clipboard.Root value={value} onChange={setValue}>
<Clipboard.Input>
<Clipboard.Trigger asChild>
<ActionIcon variant="subtle" color="neutral" size="sm" aria-label="Copy link">
<Clipboard.Indicator copied={<CheckIcon />}>
<CopyIcon />
</Clipboard.Indicator>
</ActionIcon>
</Clipboard.Trigger>
</Clipboard.Input>
</Clipboard.Root>
<Button size="xs" variant="light" onClick={() => setValue(regenerate())}>
Generate new link
</Button>Anatomy
Clipboard.Root— owns the value + copied/idle state; renders the real<div>the rest of the compound reads context from.Clipboard.Label— Ark's rawLabelpart, a real<label>. Unstyled by default — size and style it like any other field label.Clipboard.Control— Ark's rawControlpart; carriesdata-copied. Only needed if you want a different shell thanClipboard.Input's built-inInputBase.Clipboard.Input— the value, in the sharedInputBaseshell (always read-only). Pass theTriggeraschildrenso it renders as a real sibling, not through the shell'saria-hiddenendSlot.Clipboard.Trigger— Ark's rawTriggerpart, a real<button>.asChildto swap inActionIcon(or any other trigger element).Clipboard.Indicator— Ark's rawIndicatorpart. Passcopied={<CheckIcon />}to swap content once the value is copied; it reverts once the state returns to idle.Clipboard.ValueText— Ark's rawValueTextpart; renders the live value as plain text instead of a form control.
testId
Clipboard.Root renders a real <div> and takes its own testId. Clipboard.Input is a shell — like Input — so its testId lands on the real <input>, not the shell <div>. Copy's testId lands on its ActionIcon's underlying <button>. Clipboard.Label / Control / Trigger / Indicator / ValueText are raw Ark re-exports and forward an arbitrary data-testid straight through natively. See Testing.
API
Copy
Prop
Type
Clipboard.Root
Prop
Type
Clipboard.Input
Prop
Type
Accessibility
Copy's accessible name comes from Ark's own translated"Copy to clipboard"/"Copied to clipboard"trigger label — announced by screen readers as the copied state changes.- In the
Clipboardcompound,Clipboard.Triggergets that same automaticaria-labelfor free; override it viaClipboard.Root'stranslationsprop. Clipboard.Input'sTriggeris rendered as a real, focusable sibling of the input — not throughInputBase's decorativeendSlot(which isaria-hidden) — so it stays reachable by keyboard and announced to assistive tech.Clipboard.Indicator's content swap is driven by the machine's own live state, so assistive tech announcing the trigger'saria-labelchange is the primary signal — the icon swap itself is decorative.- In
Copy's headless mode, none of the above is automatic — the rendered element, its accessible name/label, and theonClickwiring are entirely yours, same as composingClipboard.Root/Clipboard.Triggerdirectly.
Changelog
Slider
Single-value or two-thumb range input on top of Ark UI's slider primitives. Marks, floating label, sizes, colors — keyboard, RTL, and ARIA handled upstream.
Checkbox
A single boolean (or tri-state) control on Ark UI's Checkbox machine — inline label, built-in description / error, native form submission, and full keyboard / ARIA.