Copy Button

A button that copies a value, such as a command, link or key, and confirms it with a check and a "Copied" announcement.
Stable
Web
iOS
Android
@advui/core
Source
JSX
<CopyButton value="pnpm add @advui/core" />
variant
size

Installation

pnpm
pnpm add @advui/core
npm
npm install @advui/core

Usage

import { CopyButton } from '@advui/core'

<CopyButton value="pnpm add @advui/core" />
<CopyButton value={inviteUrl}>Copy invite link</CopyButton>

// iOS and Android, once at app start:
import * as Clipboard from 'expo-clipboard'
import { setClipboard } from '@advui/core'
setClipboard(Clipboard.setStringAsync)

Examples

Icon button

Next to a command. The name says what it copies: “Copy command”.
pnpm add @advui/core

With a label

The label reads “Link copied” for two seconds after copying.

In an input

In a read-only field’s trailing slot.

Your own control

`useClipboard` copies and reports the status; a toast announces the result.

API reference

CopyButton

Without children, an icon button named “Copy” with a tooltip on web; with children, a Button with that label. Takes the other Button props, such as `disabled`.
PropTypeDefaultDescription
value requiredstring—The text to copy.
childrenReactNode—A visible label, e.g. “Copy link”. It reads “Copied” (`labels.copied`) for a moment after copying.
variant'default' | 'secondary' | 'outline' | 'ghost' | 'destructive' | 'link'—Same variants as Button. Default: `ghost` without children, `outline` with them, or the ButtonGroup’s.
size'sm' | 'md' | 'lg''md'Button height: 32, 40 or 48px (a square without children).
timeoutnumber2000How long the check (or the failure) shows, in ms.
onCopy(value: string) => void—Called with the text once it is on the clipboard.
onCopyError(error: unknown) => void—Called when the clipboard refuses the text, or when iOS or Android have none (`setClipboard`).
labelsPartial<CopyButtonLabels>—`copy` (the icon button’s name and tooltip, “Copy”), `copied` (“Copied”) and `failed` (“Couldn’t copy”), for translation or a more exact name such as “Copy API key”.

useClipboard(options)

For your own copy control: `const { copy, status } = useClipboard({ timeout, onCopy, onError })`. `copy(text)` resolves to whether it worked; `status` is `idle`, `copied` or `failed` (for `timeout` ms after). Announce the result yourself, e.g. with a toast.
PropTypeDefaultDescription
timeoutnumber2000How long `status` stays `copied` or `failed`, in ms.
onCopy(text: string) => void—Called after a copy.
onError(error: unknown) => void—Called when a copy fails.

setClipboard(write)

Registers the app’s clipboard for every Copy Button and Code Block, once at app start: `setClipboard(Clipboard.setStringAsync)` with `expo-clipboard`. iOS and Android need one; web uses the browser’s clipboard unless one is set. `writeClipboard(text)` copies with it directly.
PropTypeDefaultDescription
write required((text: string) => Promise<unknown> | void) | undefined—Writes the text; throw or reject when it cannot.

Accessibility

  • A button, named “Copy” (labels.copy) without children or by its label with them. Give it a name that says what it copies when there are several, e.g. “Copy API key”.
  • After a press the result is announced, “Copied” or “Couldn’t copy”: through a polite live region on web, and with AccessibilityInfo.announceForAccessibility on iOS and Android. Android 13 and later confirm every copy themselves, and TalkBack reads it, so there only a failure is announced. The icon button keeps its name; its tooltip shows the result.
  • The check is not the only sign: the tooltip or label text changes too.
Keyboard interactions
KeyAction
Enter / SpaceCopies the value.

Platform notes

Web
Uses the Clipboard API, and falls back to a hidden text area and execCommand("copy") where the page is not served over HTTPS.
iOS
Needs setClipboard (e.g. expo-clipboard). Without it the press fails, onCopyError is called and a warning is logged once.
Android
Needs setClipboard, as on iOS. Android 13 and later show their own “Copied” preview as well.
Since v0.14.0Last updated Nov 2, 2026Edit this page on GitHub