Utilities
useCopy
A hook that copies text to the clipboard and holds the state of a copy button.
Utilities
A hook that copies text to the clipboard and holds the state of a copy button.
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>
)
}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.
import { Button, useCopy } from 'ferry-ui'
function CopyInviteLink({ url }: { url: string }) {
const [copied, copy] = useCopy()
return <Button onClick={() => void copy(url)}>{copied ? 'Link copied' : 'Copy invite link'}</Button>
}CopyButton, CopyField and SecretField. Code Block has its own copy button. They all use this hook.Toaster. When a copy fails, the hook shows an error toast. The Toast page tells how to mount Toaster.copied stays true for 1500 milliseconds. Pass timeout to change this time.
ord_2041_7c9eimport { 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>
)
}When a copy fails, the hook shows the toast "Could not copy to the clipboard". Pass errorMessage to change this text.
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.
const [copied, copy] = useCopy({ onError: (text) => reportCopyError(text) })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.
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>
)
}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(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. |