CopyButton is a thin specialisation of <Button> that owns a single concern:
take a string, write it to the clipboard, and show the user it worked. It
renders a square icon button (no label), flips its glyph from an empty task
circle to a checked one for two seconds, and fires a toast so the confirmation
is announced even when the button is out of the user’s gaze.
Reach for it wherever a user needs to grab a literal value out of the UI: an
API key they just minted, a webhook signing secret, a resource identifier in
a details panel. The primary real-world home is the copyButton slot on
<Code>, where it sits flush against the code it copies. Standalone usage
works too, but the default size="icon" is tuned for sitting next to other
chrome, not anchoring a form.
Import
import { CopyButton } from "@unkey/ui";
Standalone
The component renders as a small icon-only button. Click it and the clipboard
receives value; the glyph flips to a checkmark, and two seconds later it
flips back.
The toast this emits travels through @unkey/ui’s toaster. This design app
does not mount a <Toaster>, so you won’t see the toast pop up here. The
clipboard write itself still works. Mount a <Toaster> at the root of your
app to get the full experience.
Inside a Code block
The common case. Pass a <CopyButton> into the copyButton slot on <Code>
and it pins to the top-right of the block, right where users look for it.
unkey_3ZaM7pQ1xK9VdLb2wTgRcYCustom toast description
toastMessage is forwarded to the toast’s description field. Use it when
the copied value carries a caveat the user should see on confirmation
(a secret that should be treated as a password, a key that expires soon, a
URL that only works from one environment).
Accessibility
CopyButton carries aria-label="Copy to clipboard" and a visually hidden
"Copy" text node inside the button, so screen readers announce it clearly
even though the visual label is an icon. The title attribute doubles as a
native tooltip on hover. Because the underlying element is a real <button>
with type="button", it participates in tab order and activates on both
Enter and Space.
Two details worth knowing. The click handler calls e.stopPropagation()
before running, so placing a CopyButton inside another clickable surface
(a table row, a card) won’t also trigger the parent’s onClick. And the
two-second confirmation state is purely visual; the authoritative “it
worked” signal is the toast, which screen readers pick up via the toaster’s
live region.
Props
CopyButton extends <Button>, so every Button prop (variant, color,
size, disabled, loading, className, the full set of
ButtonHTMLAttributes) passes through. The three additions below are what
makes it a CopyButton rather than a plain Button.
value string Required The string written to the clipboard on click. Evaluated at click time, so it’s safe to pass a value that changes across renders.
toastMessage string Optional Optional description shown beneath the “Copied to clipboard” toast title. Use it to add context the user should read on confirmation (e.g. “Treat this like a password”).
src string Optional Free-form tag attached to the copy action for analytics — the component where the copy originated. Not user-visible.
variant "primary" | "outline" | "ghost" Optional
Default "outline" Inherited from Button, but CopyButton defaults to outline so the
affordance reads as a secondary action.
size "sm" | "md" | "lg" | "xlg" | "2xlg" | "icon" Optional
Default "icon" Inherited from Button; pinned to icon so the button stays square and
the glyph centres correctly. Overriding this is supported but the
rendered content is a single icon, so non-icon sizes will feel loose.
onClick (e: MouseEvent<HTMLButtonElement>) => void Optional Called after the clipboard write and the confirmation flip. Call
e.preventDefault() in a parent handler to skip the copy entirely.
className string Optional Additional Tailwind classes. Merged with the component’s own classes
(which include focus:ring-0 focus:border-grayA-6 secret) via cn;
later classes win.
...rest ButtonProps Optional Every other prop accepted by <Button> passes through unchanged —
color, disabled, loading, aria-*, data-*, and the full set of
ButtonHTMLAttributes<HTMLButtonElement>.
Related
- Button — the base component CopyButton extends. Reach for it directly when the copy action needs a visible text label.
- Code — the most common host for CopyButton, via its
copyButtonslot. - Toast — where the “Copied to clipboard” confirmation surfaces. Mount a
<Toaster>at your app root so the toast is visible.