Actions
Copy Button
A clipboard action with transient success feedback and icon morphing.
Install
npx shadcn@latest add https://4af.selfsimilar.dev/r/copy-button.jsonUsage
import { CopyButton } from "@/components/ui/copy-button";
<CopyButton value="Text to copy" size="md">Copy</CopyButton>API
Main export: CopyButton
An Action Button that copies text to the clipboard, with visual and screen-reader confirmation.
size accepts xs, sm, md, or lg. With a label these are the standard button sizes;
without one they are the matching square icon-xs, icon-sm, icon, or icon-lg sizes. An
icon-only button defaults to icon. With a label, the icon gap matches Button (8, 10, or 12px).
Resolve values on press
value takes a function as well as a string:
value: string | (() => string | Promise<string>)Use the function form when the text is assembled from a selection, read from the DOM, or fetched. It runs on press, so nothing is computed or requested on every render.
<CopyButton value={() => selected.map((row) => row.url).join("\n")} />
<CopyButton value={async () => (await fetchDocument(id)).body} />The loader appears if resolving the value and writing it to the clipboard takes longer than
pendingDelay (50ms by default). A function that throws or rejects shows the error state and
writes nothing, so onCopy only reports text that was copied.
icon replaces the resting glyph, for example to tell apart two copy buttons that copy
different things. The success check is unchanged.
<CopyButton icon={<Link />} value={() => rows.map((row) => row.url).join("\n")} />Copying from somewhere else
writeToClipboard(text) is exported for clipboard writes outside the button, such as a menu
row. navigator.clipboard only exists in secure contexts, so on plain HTTP it falls back to
execCommand. It rejects on failure instead of returning false.
await writeToClipboard(item.url);