Actions
CopyButton
A button that puts a piece of text on the clipboard: an API key, a link, a command. On a click it copies value, then for two seconds shows a check and says "Copied" (or a cross and "Couldn't copy" when the browser refuses), and screen readers hear the same, politely, once. It uses the Clipboard API, and a text selection where that is not available.
In Spoonjoy
Spoonjoy, a recipe app, is built with Ouro HX (how).
See it live, no sign-in needed: Developers · API playground
In its source: app/routes/developers.playground.tsx
Install
Once per app, with Tailwind CSS v4 and an @/* path alias (Ouro HX is built on Base UI, so --base base; skip -t in an existing app):
npx shadcn@latest init --base base --preset nova --yes --no-monoreponpx shadcn@latest add ourostack/ouro-ui/ouro-base#v1.2.13Then the component. From v0.8.0 it installs only the helpers it imports and never touches your stylesheet (Installing):
npx shadcn@latest add ourostack/ouro-ui/copy-button#v1.2.13Or, with the @ouro namespace set up:
npx shadcn@latest add @ouro/ouro-base @ouro/copy-buttonIts tests, stories and Playwright spec are optional. They need Vitest 4.1.11 or later, and from v0.8.0 never change a version your app has (Testing):
npx shadcn@latest add ourostack/ouro-ui/copy-button#v1.2.13 ourostack/ouro-ui/copy-button-tests#v1.2.13import { CopyButton } from "@/components/ouro/copy-button/copy-button";Paths use shadcn's default @/ alias; the CLI rewrites them to yours.
Example
<CopyButton value={apiKey} label="Copy key" />
<CopyButton value={url} label="Copy link" hideLabel variant="plain" />Use instead when
- Use
CodeBlockinstead for code or a command shown with its own copy button.
Props
Type: CopyButtonProps.
className- Type:
string - Layout only: margins, grid placement (
ms-auto). Appearance classes warn in development. copiedLabel- Type:
string - Default:
"Copied" - What the button says, and screen readers hear, once the text is on the clipboard.
failedLabel- Type:
string - Default:
"Couldn't copy" - What the button says, and screen readers hear, when the browser refused to copy.
hideLabel- Type:
boolean - Shows only the icon; the label stays the button's name for screen readers. For a corner of a code block or a field.
label- Type:
string - Default:
"Copy" - The button's label, and its name for screen readers when
hideLabelis set. onCopy- Type:
((value: string) => void) - Called with the text once it is on the clipboard.
ref- Type:
Ref<HTMLButtonElement> - Reaches the rendered element.
size- Type:
"sm" | "md" | "lg" - Default:
"sm" - Control size, as Button's.
value(required)- Type:
string - The text the button puts on the clipboard.
variant- Type:
"solid" | "soft" | "outline" | "plain" - Default:
"outline" - Visual weight, as Button's.
Native attributes
Also accepts the attributes in React's ButtonHTMLAttributes and HTMLAttributes (id, aria-*, event handlers), passed to the rendered element, except those named on this page.
Don't use
These are not props. Passing one fails the type check with the fix in the error, and in development a console message names the fix.
children: Not a prop. The label islabel:<CopyButton value={url} label="Copy link" />.content: Not a prop. The text to copy isvalue:<CopyButton value={token} />.onClick: Not a prop. CopyButton copies on click itself; to react to a copy, passonCopy.text: Not a prop. The text to copy isvalue:<CopyButton value={token} />.timeout: Not a prop. The confirmation iscopiedLabel, and it lasts two seconds.
className is for layout only
Pass margins, width and grid placement (mt-4, w-full, col-span-2). Your classes always win over Ouro UI's: CopyButton merges className last with cn (tailwind-merge), so a class that conflicts with its own replaces it.layout-classes
Appearance comes from props, not classes. In development, a class that sets appearance logs one warning per component instance, naming the prop to use instead. A class sets appearance when, after its variants (hover:, sm:) and any ! are removed, it starts with bg-, text-, border-, rounded-, shadow-, font-, ring-, from-, via-, to-, fill- or stroke-, or is border, rounded, shadow or ring, or has a dark: variant. Text wrapping, overflow and table layout classes (text-wrap, text-ellipsis, border-collapse) are layout and do not warn.
Alignment classes (text-center) warn too: set alignment on a parent element.
Claim sources
- layout-classes: test:registry/ouro/components/ouro/copy-button/copy-button.test.tsx::CopyButton > forwards a ref, takes button attributes, and lets app layout classes win
verify:testExists(registry/ouro/components/ouro/copy-button/copy-button.test.tsx::CopyButton > forwards a ref, takes button attributes, and lets app layout classes win)