# useCopy

A hook that copies text to the clipboard and holds the state of a copy button.

```tsx
import { Button, useCopy } from 'ferry-ui'
import { Check, Link2 } from 'lucide-react'

export default function UseCopyHero() {
  const [copied, copy] = useCopy()
  return (
    <Button icon={copied ? <Check /> : <Link2 />} onClick={() => void copy('https://example.com/invite/8f3a2c91')}>
      {copied ? 'Link copied' : 'Copy invite link'}
    </Button>
  )
}
```

## Usage

`useCopy()` returns the pair `[copied, copy]`. `copy(text)` writes the text to the clipboard. After a copy with no error, `copied` is `true` for a short time.

```tsx

function CopyInviteLink({ url }: { url: string }) {
  const [copied, copy] = useCopy()
  return <Button onClick={() => void copy(url)}>{copied ? 'Link copied' : 'Copy invite link'}</Button>
}
```

- **Use the copy components first.** [Copy](/docs/components/copy) has `CopyButton`, `CopyField` and `SecretField`. [Code Block](/docs/components/code-block) has its own copy button. They all use this hook.
- **Mount a `Toaster`.** When a copy fails, the hook shows an error toast. The [Toast](/docs/components/toast) page tells how to mount `Toaster`.

## Examples

### Feedback time

`copied` stays `true` for 1500 milliseconds. Pass `timeout` to change this time.

```tsx
import { Button, useCopy } from 'ferry-ui'
import { Check, Copy } from 'lucide-react'

export default function UseCopyTimeout() {
  // `copied` stays true for 4 seconds, not for 1.5 seconds.
  const [copied, copy] = useCopy({ timeout: 4000 })
  return (
    <div className="flex items-center gap-3">
      <code className="font-mono text-[13px] text-foreground">ord_2041_7c9e</code>
      <Button size="tiny" icon={copied ? <Check /> : <Copy />} onClick={() => void copy('ord_2041_7c9e')}>
        {copied ? 'Copied' : 'Copy order ID'}
      </Button>
    </div>
  )
}
```

### Failed copy

When a copy fails, the hook shows the toast "Could not copy to the clipboard". Pass `errorMessage` to change this text.

```tsx
const [copied, copy] = useCopy({ errorMessage: 'Copy the link by hand' })
```

Pass `onError` to replace the toast with your code. The function gets the text of the failed copy.

```tsx
const [copied, copy] = useCopy({ onError: (text) => reportCopyError(text) })
```

### Copy with no hook

`copyText(text)` is the function that the hook calls. Use it outside React, or when your code gives the feedback. Its promise gives `true` or `false`. It does not throw.

```tsx
import { Button, DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger, copyText, toast } from 'ferry-ui'
import { MoreHorizontal } from 'lucide-react'

export default function UseCopyCopyText() {
  // No hook: copyText() gives the result, and this code shows its own messages.
  const copyId = async () => {
    const ok = await copyText('inv_2041')
    if (ok) toast.success('Invoice ID copied')
    else toast.error('Could not copy the invoice ID')
  }

  return (
    <DropdownMenu>
      <DropdownMenuTrigger asChild>
        <Button variant="ghost" size="icon" icon={<MoreHorizontal />} aria-label="Actions for INV-2041" />
      </DropdownMenuTrigger>
      <DropdownMenuContent align="end">
        <DropdownMenuItem onSelect={() => void copyId()}>Copy invoice ID</DropdownMenuItem>
      </DropdownMenuContent>
    </DropdownMenu>
  )
}
```

## API reference

### useCopy

`useCopy(options)` returns `[copied, copy]`. The `options` object is optional. Its type is `UseCopyOptions`.

| Option | Type | Default | Role |
| --- | --- | --- | --- |
| `timeout` | `number` | `1500` | The time that `copied` stays `true`, in milliseconds. |
| `errorMessage` | `string` | `'Could not copy to the clipboard'` | The text of the error toast. |
| `onError` | `(text: string) => void` | | Runs when the copy fails, with the text. It replaces the error toast. |

| Value | Type | Role |
| --- | --- | --- |
| `copied` | `boolean` | `true` after a copy, for the time of `timeout`. |
| `copy` | `(text: string) => Promise<void>` | Copies the text. The function keeps the same identity between renders. |

### copyText

`copyText(text)` returns a `Promise<boolean>`. It uses the Clipboard API of the browser. On a page with no secure context (plain HTTP), it uses a hidden `<textarea>`.

| Parameter | Type | Role |
| --- | --- | --- |
| `text` | `string` | The text to copy. |
