# Copy Button (/ui/components/react/copy-button)



<Playground component="copy-button" />

## Usage [#usage]

```tsx
import { CopyButton } from '@appica/ui-react/copy-button'
```

```tsx
<CopyButton value="npm install @appica/ui-react" />
```

`CopyButton` writes `value` to the clipboard on click, then swaps its copy icon for an animated check for a couple of seconds. It's built on [`Button`](/ui/components/react/button), so it takes every Button prop (`variant`, `size`, `disabled`, …) — it just defaults to a `ghost` `icon-sm` icon button.

`value` is flexible: a **string**, a **function** returning a string (or a promise of one — handy for computing the text lazily), or a **ref** to an element, in which case its `value` (inputs/textareas) or `textContent` is copied. That makes it a natural fit in the corner of a code block or input.

## Examples [#examples]

### Variants [#variants]

`CopyButton` forwards `variant` (and any Button prop) straight through.

```tsx
import { CopyButton } from '@appica/ui-react/copy-button'

export default function CopyButtonVariants() {
  return (
    <div className="flex flex-wrap items-center gap-3">
      <CopyButton value="Ghost" variant="ghost" />
      <CopyButton value="Soft" variant="soft" />
      <CopyButton value="Outline" variant="outline" />
      <CopyButton value="Primary Outline" variant="primary-outline" />
      <CopyButton value="Primary" variant="primary" />
      <CopyButton value="Secondary" variant="secondary" />
    </div>
  )
}
```

### Sizes [#sizes]

Size comes from [`Button`](/ui/components/react/button): use the square `icon-sm` / `icon-md` / `icon-lg` for an icon-only button (the default is `icon-sm`), or `sm` / `md` / `lg` when you add a text label.

```tsx
import { CopyButton } from '@appica/ui-react/copy-button'

export default function CopyButtonSizes() {
  return (
    <div className="flex items-center gap-3">
      <CopyButton value="Small" variant="outline" size="icon-sm" />
      <CopyButton value="Medium" variant="outline" size="icon-md" />
      <CopyButton value="Large" variant="outline" size="icon-lg" />
    </div>
  )
}
```

### With a label [#with-a-label]

Pass children to show text beside the icon. A string label automatically swaps to `copiedLabel` on success, mirroring the check animation.

```tsx
import { CopyButton } from '@appica/ui-react/copy-button'

export default function CopyButtonWithLabel() {
  return (
    <CopyButton value="npm install @appica/ui-react" variant="outline" size="sm" copiedLabel="Copied!">
      Copy
    </CopyButton>
  )
}
```

### Copy from an element [#copy-from-an-element]

Give `value` a ref instead of a string to copy another element's content — here a code snippet. The button copies the element's `textContent` (or the `value` of an input/textarea).

```tsx
'use client'

import { useRef } from 'react'
import { CopyButton } from '@appica/ui-react/copy-button'

export default function CopyButtonFromElement() {
  const codeRef = useRef<HTMLElement>(null)
  return (
    <div className="border-border-muted bg-background-subtle relative w-full max-w-90 rounded-lg border py-3 ps-4 pe-12">
      <code ref={codeRef} className="text-foreground font-mono text-sm">
        npm install @appica/ui-react
      </code>
      <div className="absolute inset-e-2 top-1/2 -translate-y-1/2">
        <CopyButton value={codeRef} label="Copy command" />
      </div>
    </div>
  )
}
```

## RTL [#rtl]

Every Appica UI component supports right-to-left layouts out of the box. Set the `dir` attribute on a container (commonly your `<html>` element) so CSS logical properties resolve correctly, and wrap your tree in `DirectionProvider` so direction-aware behavior — roving focus, popup placement, and the like — follows the same direction.

```tsx
import { DirectionProvider } from '@appica/ui-react/providers/direction-provider'

export default function RootLayout({ children }) {
  return (
    <html lang="ar" dir="rtl">
      <body>
        <DirectionProvider dir="rtl">{children}</DirectionProvider>
      </body>
    </html>
  )
}
```

The icon and label swap sides to follow the resolved direction. For setup details and caveats, see the [RTL guide](/ui/docs/react/rtl).

<RtlPreview component="copy-button" />

## API reference [#api-reference]

### CopyButton [#copybutton]

Extends [`Button`](/ui/components/react/button) — every Button prop applies. The props below are the copy-specific additions and defaults.

| Prop          | <ColMinWidth width="200">Type</ColMinWidth>                                          | Default     | <ColMinWidth width="220">Description</ColMinWidth>                                                          |
| ------------- | ------------------------------------------------------------------------------------ | ----------- | ----------------------------------------------------------------------------------------------------------- |
| `value`       | <Code>string \| RefObject\<HTMLElement> \| (() => string \| Promise\<string>)</Code> | —           | **Required.** What to copy: a string, an element ref (its value/textContent), or a (possibly async) getter. |
| `label`       | `string`                                                                             | `'Copy'`    | Accessible name (and tooltip via `title`) in the idle state.                                                |
| `copiedLabel` | `string`                                                                             | `'Copied'`  | Accessible name after a successful copy; a string child also swaps to this.                                 |
| `timeout`     | `number`                                                                             | `2000`      | How long (ms) the copied state lasts before reverting.                                                      |
| `onCopy`      | <Code>(value: string) => void</Code>                                                 | —           | Called with the copied text on success.                                                                     |
| `onCopyError` | <Code>(error: unknown) => void</Code>                                                | —           | Called if reading the value or writing to the clipboard fails.                                              |
| `variant`     | <Code>Button variant</Code>                                                          | `'ghost'`   | Inherited from `Button`; defaults to `ghost`.                                                               |
| `size`        | <Code>Button size</Code>                                                             | `'icon-sm'` | Inherited from `Button`; defaults to the small icon size.                                                   |
| `children`    | `ReactNode`                                                                          | —           | Optional visible label beside the icon.                                                                     |

`CopyButton` falls back to a hidden-`textarea` + `execCommand('copy')` when the async Clipboard API is unavailable, and exposes `data-copied` while in the copied state for styling.

## Accessibility [#accessibility]

* Renders a real `<button>` with an accessible name from `label` (and `copiedLabel` once copied), so an icon-only button is still announced.
* The copied state is conveyed by the accessible-name change, not color alone.
* `disabled` (a Button prop) blocks the copy and exposes `data-disabled` for styling.
* The icon cross-fade and check-draw animations honour `prefers-reduced-motion`.
