# Copy button

An icon button that copies a value to the clipboard and confirms the result. Use it next to IDs, keys, and commands.

```tsx
// demo.tsx
import {CopyButton, Stack, Text} from '@earendil-works/elanor-react';

export default function CopyButtonDemo() {
  return (
    <Stack direction="row" align="center" gap="small">
      <Text render={<span />}>workspace-2048</Text>
      <CopyButton value="workspace-2048" aria-label="Copy workspace ID" />
    </Stack>
  );
}
```

## Usage

```tsx
import {CopyButton} from '@earendil-works/elanor-react';

<CopyButton value="workspace-2048" aria-label="Copy workspace ID" />;
```

Name the button after what it copies. On success, the icon changes to a check for two seconds; screen readers hear `copiedLabel`, or `errorLabel` if copying fails. Clipboard access requires HTTPS or localhost.

For code, pass a Copy button to [Code snippet](/components/code-snippet.md)'s `actions`.

## Value from the browser

When the value depends on the page, such as an absolute URL, pass a function to `value`; it runs when the button is pressed. Pass `icon` to show another icon before copying; the check still confirms the copy. [Linked heading](/components/linked-heading.md) uses both to copy a heading's link.

```tsx
<CopyButton
  value={() => new URL('#billing', window.location.href).href}
  icon={<LinkIcon />}
  aria-label="Copy link to Billing"
/>
```

## Static rendering

On pages rendered on the server without React in the browser, its behavior, `createCopyButton`, copies instead. The button carries what it needs: `data-elanor="copy-button"` and its value, labels, and state as attributes. Bind it once the page has loaded:

```ts
import {createCopyButton, enhance} from '@earendil-works/elanor-react';

enhance({'copy-button': createCopyButton});
```

It copies, confirms, and announces like the React component. A function `value` only runs with React, so on static pages pass a string. The server-rendered button repeats the value in an attribute; once React hydrates it, the attributes go away and the behavior stands down, so pages with both static buttons and hydrated ones can call `enhance` on the whole document.

## API

### `CopyButton`

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `value` (required) | `string \| (() => string)` | - | Text written to the clipboard, or a function that returns it when the button is pressed, for values only known in the browser, such as an absolute URL. |
| `icon` | `ReactNode` | `<ContentCopyIcon />` | Icon shown before copying; a check replaces it after a copy. Defaults to the copy icon. |
| `copiedLabel` | `string` | `'Copied.'` | Announced after a successful copy. |
| `errorLabel` | `string` | `'Could not copy. Try again or copy manually.'` | Announced when copying fails. |
| `onCopySuccess` | `() => void` | - |  |
| `onCopyError` | `(error: Error) => void` | - |  |
| `data-elanor` | `string` | - | The page's own behaviors for the button, attached after the copy button's. |
| `data-elanor-copy-link` | `string` | - | A link the behavior copies as an absolute URL on static pages, for a `value` that is a function. |
| `aria-label` | `string` | `'Copy'` | Defines a string value that labels the current element. |

Also accepts Base UI [`Button`](https://base-ui.com/react/components/button.md) props.
