# Copy Button (/docs/copy-button)

A clipboard action with transient success feedback and icon morphing.

## Install

```bash
npx shadcn@latest add https://4af.selfsimilar.dev/r/copy-button.json
```

## Usage

```tsx
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:

```tsx
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.

```tsx
<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.

```tsx
<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.

```tsx
await writeToClipboard(item.url);
```