Copy Button
Copy a value to the clipboard with an icon button or a labeled button that confirms with a check, a tooltip and an announcement.
- Client portal
- portal.northlight.studio
- Status
- Live since March 4
Installation
pnpm dlx shadcn@4.18.0 add @siteplane/copy-buttonComplete the one-time setup first. Registry JSON lists source files and dependencies.
For this page’s examples, also install:
pnpm dlx shadcn@4.18.0 add @siteplane/card @siteplane/data-list @siteplane/input @siteplane/input-groupField is installed with Input.
Usage
import { CopyButton } from "@/siteplane/components/copy-button";
<CopyButton value="https://portal.northlight.studio" />;Icon only
Without children, CopyButton shows the copy icon alone, as the muted inline action: the bare icon in a 24 px target, muted at rest and darker on hover, press and keyboard focus. Place it after the value it copies, such as an address, an ID or an invite link. Name it after what it copies with labels.copy or aria-label, such as “Copy address”.
A press writes value to the clipboard. The icon turns into a check for two seconds and then draws itself again, the tooltip changes from “Copy” to “Copied”, and screen readers hear “Copied”.
With text
Children make a text button: the icon leads and the label follows. While the check shows, the label reads labels.copied, “Copied” by default. Set variant for the look of another Button, such as outline.
In an input group
Inside a field, set profile="field-action", the profile of every icon action in a field. The icon sits at the end of the field, as the show and hide action of a password does.
Copied and failed
A copy that works shows the check. A copy that fails, because the browser has no clipboard, refuses it, or the value could not be loaded, says labels.copyFailed, “Copy unavailable” by default, for a moment: as a cross with that tooltip on the icon alone, in the label of a text button, and to screen readers. onCopy receives the copied text, and onCopyError receives the error, for example to show a toast with what to do next.
A value that loads
value can be a function that returns the text, also asynchronously, such as a token requested when the button is pressed. Browsers allow a copy only shortly after the press, so keep that request short. In Safari the text goes in as a pending clipboard item, which keeps the press valid while it loads.
<CopyButton labels={{ copy: "Copy API token" }} value={() => fetchToken()} />Looks
Button props pass through. A variant alone, such as outline or ghost, gives the CopyButton the look of that Button, at the icon square of its size. profile, shape, className and render work as on Button.
<CopyButton size="icon-md" value={hex} variant="outline" />tooltip={false} turns the tooltip off. An object passes props to TooltipPopup, such as side.
Copy from a menu
A menu entry that copies stays a MenuItem with the Copy icon. Copy in its onClick with useCopyToClipboard, the copy of CopyButton without the button:
const { copy } = useCopyToClipboard({ labels: { copied: "Link copied" } });
<MenuItem onClick={() => copy(link)}>
<CopyIcon />
Copy link
</MenuItem>;The hook announces the outcome. copied and failed tell a control of your own which state to show.
API reference
| Prop | Type | Default | Description |
|---|---|---|---|
Copy | string | (() => string | Promise<string>) | required | The text to copy, or a function that returns it when the button is pressed. |
Copy | ReactNode | unset | A label after the icon, which makes a text button. Without one, the button shows the icon alone with a tooltip. |
Copy | Partial<CopyButtonLabels> | English | Replaces the words: copy (Copy, the name and tooltip of the icon alone), copied (Copied) and copyFailed (Copy unavailable). |
Copy | (value: string) => void | unset | Called with the text once it is on the clipboard. |
Copy | (error: unknown) => void | unset | Called with the error when the clipboard is missing or refuses, or the value cannot be loaded. |
Copy | boolean | CopyButtonTooltipProps | true | The tooltip of the icon alone. false turns it off; an object passes props to TooltipPopup. |
Copy | Button props | muted inline action | Without profile and variant, the icon alone is the muted inline action at icon-xs. A variant gives the look of that Button; profile field-action places it inside a field. |
use | (options?) => { copy, copied, failed, phase } | announce: true | The copy of CopyButton for another control. Options: announce, labels (copied, copyFailed), onCopy and onCopyError. copy(value) resolves true once the text is on the clipboard. |
CopyButtonProps extends the Button props except value, children and the
native onCopy, and accepts a ref. aria-label names the icon alone at rest,
in place of labels.copy. The check shows for two seconds and a failure for a
little longer; both are said once in a polite live region, which stays readable
while a dialog is open. The tooltip stays open on a press, so it can say
“Copied”, and is hidden from assistive technology, which hears the button’s
name. The type CopyButtonLabels is exported.
Usage notes
Browsers let a page write to the clipboard only in a secure context, on https or localhost, and in response to a press. Elsewhere, such as on plain http, CopyButton copies through a text selection, which a browser may refuse; the failure state then shows.
Show the value next to the button, or in the field it belongs to, so people can still select it when a copy fails. For a block of code, set copyable on CodeBlock, which places this button in the block.