Skip to content
shadcn.io

shadcn.io is not affiliated with official shadcn/ui

Buttons

Free

Copy Button for React

A button that copies text to the clipboard and swaps its icon for a check. For React with Base UI, next to code, links and keys people need to paste somewhere else.

Framework
UI library

Press it and content goes to the clipboard: the copy icon turns into a check for three seconds, then comes back. If the clipboard refuses, a cross shows instead, so nobody walks away thinking they copied something they didn't.

Use it beside an install command, a share link, an API key or an order number. It wraps the Button, so variant and size work as usual; it's icon-only by default, and a label next to the icon makes it a regular button. The icons cross-fade and scale with CSS transitions, with no animation library, and with reduced motion they only fade.

Preview width

Installation

Sign in to get your install command
Free with an account. It carries your token, so the CLI installs everything in one go.

Usage

import { CopyButton } from "@/components/ui/copy-button";
<CopyButton aria-label="Copy link" content="https://example.com/invite/7Hq2" />

Demos

Reset Delay

resetDelay is how long the check stays, in milliseconds, and the default is 3000. A short one suits a value people copy again and again; a long one gives time to read the check before switching windows.

Preview width

Controlled

Pass copied and onCopiedChange to hold the copied state yourself. The button calls onCopiedChange(true) when the copy lands and onCopiedChange(false) when the check goes, so the hint below the address can follow it.

Preview width

Accessibility

The icons are hidden from assistive tech. An icon-only button needs an aria-label that says what it copies, such as Copy command. After each press, a polite status inside the button says Copied, or Couldn't copy when the clipboard refuses; change those words with copiedLabel and failedLabel.

Enter
Copy the text.
Space
Copy the text.

FAQ

What happens when the copy fails?

The icon becomes a cross for resetDelay, the status says Couldn't copy, and onCopyError gets the error. It fails when the browser denies clipboard access or the page isn't served over HTTPS, where there's no clipboard to write to.

Can I check the text before it's copied?

Yes. onCopyRequest gets the text first; return false to skip the copy, say to validate it or copy it another way. It's also the place for analytics.

Can I keep the copied state myself?

Pass copied and onCopiedChange. The button shows what copied says and asks for changes through onCopiedChange. The Controlled demo uses it for a hint below the field.

What if someone presses it again while the check shows?

It copies again and the check's timer starts over. With no content, a press does nothing.

Can it have a label and not just an icon?

Yes. Put the label in the children, such as Copy link: the icon sits before it and the size switches from icon-md to md. Pass size to choose another. Your own onClick still runs, before the copy.

What happens with reduced motion?

The icons swap with a plain fade, without scaling. The check and the cross still show.

API Reference

CopyButton

data-slot="copy-button-icon"

PropType
copied?boolean

Whether the check shows, to hold that state yourself. Pair it with onCopiedChange.

onCopiedChange?(copied: boolean) → void

Called when the check shows (true) and when it goes (false).

Parameters

copied – boolean

Returns
void
children?ReactNode

The label beside the icon; leave it out for an icon-only button.

content?string

The text to copy. Without it, pressing the button does nothing.

copiedLabel?string

What screen readers hear once the text is on the clipboard.

Default
"Copied"
failedLabel?string

What screen readers hear when the clipboard refuses the text.

Default
"Couldn't copy"
focusableWhenDisabled?boolean

Whether the button should be focusable when disabled.

Default
false
nativeButton?boolean

Whether the component renders a native <button> element when replacing it via the render prop. Set to false if the rendered element is not a button (for example, <div>).

Default
true
onCopyError?(error: unknown) → void

Called when the clipboard refuses the text: no permission, or a page not served over HTTPS.

Parameters

error – unknown

Returns
void
onCopyRequest?(content: string) → boolean

Called with the text when a press asks to copy it. Return false to cancel the copy, after validating it or to copy it some other way.

Parameters

content – string

Returns
boolean
resetDelay?number

How long the check (or the cross, after a failed copy) stays before the copy icon comes back, in milliseconds.

Default
3000
render?ReactElement | ((props: object, state: object) => ReactElement)

Replace the host element or compose it with another component.

Type
ReactElement | ((props: object, state: object) => ReactElement)
className?string

Merged with the part's classes.

AttributeType
data-state?string

Mirrors shown.

All 35 buttons