Copy Button
A button that copies text to the clipboard, then swaps its icon for a check mark that draws itself.
Preview
Usage
import { CopyButton } from '@vitnode/core/components/ui/copy-button'<CopyButton content="pnpm create vitnode-app@canary" />The check stays for delay (2000ms). Each new copy restarts the timer.
With a label
Children show next to the icon and become the accessible name.
<CopyButton content={inviteLink} variant="secondary">
Copy invite link
</CopyButton>Icon only
Without children it's labelled "Copy" / "Copied". With several on a screen,
give each its own aria-label.
<CopyButton
aria-label="Copy install command"
content={command}
size="icon-sm"
variant="ghost"
/>Controlled
https://vitnode.com/invite/7Hq2Share this link to invite people to your community.
Use copied and onCopiedChange when the page should react.
const [copied, setCopied] = React.useState(false)
<CopyButton content={inviteLink} copied={copied} onCopiedChange={setCopied} />Async content
Text that has to be fetched first? Pass a function instead of a string. It runs
on click, the button is aria-busy while it waits, and extra clicks are ignored
until it settles.
<CopyButton
content={async () => {
const res = await fetch('/docs/dev.md')
if (!res.ok) throw new Error(`Failed to load ${res.url}`)
return await res.text()
}}
size="sm"
variant="secondary"
>
Copy Markdown
</CopyButton>Safari only lets a page write to the clipboard during the click itself, so the
pending text goes in as a ClipboardItem promise. Browsers without
ClipboardItem wait for the text, then copy it. A rejected promise shows the
same error toast as a blocked clipboard.
Accessibility
- A polite live region announces "Copied".
- With reduced motion, icons just fade and the check appears fully drawn.
- A blocked clipboard (say, on plain HTTP) shows an error toast.
event.preventDefault()in your ownonClickskips the copy.
Props
Prop
Type