useCopyToClipboard
Provides a function to copy text to the clipboard and tracks the copied state.
Code
1import { useState, useEffect } from "react"
2
3type CopiedValue = string | null
4type CopyFn = (text: string) => Promise<boolean>
5
6export function useCopyToClipboard(): [CopiedValue, CopyFn] {
7 const [copiedText, setCopiedText] = useState<CopiedValue>(null)
8
9 const copy: CopyFn = async (text) => {
10 if (!navigator?.clipboard) {
11 console.warn("Clipboard not supported")
12 return false
13 }
14
15 try {
16 await navigator.clipboard.writeText(text)
17 setCopiedText(text)
18 return true
19 } catch (error) {
20 console.warn("Copy failed", error)
21 setCopiedText(null)
22 return false
23 }
24 }
25
26 return [copiedText, copy]
27}
Usage Example
import { useCopyToClipboard } from "@/hooks/use-copy-to-clipboard"
import { Button } from "@/components/ui/button"
import { Input } from "@/components/ui/input"
import { Label } from "@/components/ui/label"
import { CheckIcon, CopyIcon } from 'lucide-react'
import { useState } from "react"
export function CopyToClipboardExample() {
const [textToCopy, setTextToCopy] = useState("Hello npui!")
const [copiedText, copy] = useCopyToClipboard()
const [isCopied, setIsCopied] = useState(false)
const handleCopy = async () => {
const success = await copy(textToCopy)
if (success) {
setIsCopied(true)
setTimeout(() => setIsCopied(false), 2000)
}
}
return (
<div className="space-y-4">
<Label htmlFor="copy-input">Text to Copy</Label>
<div className="flex gap-2">
<Input
id="copy-input"
value={textToCopy}
onChange={(e) => setTextToCopy(e.target.value)}
className="flex-1"
/>
<Button onClick={handleCopy} disabled={isCopied}>
{isCopied ? <CheckIcon className="h-4 w-4 mr-2" /> : <CopyIcon className="h-4 w-4 mr-2" />}
{isCopied ? "Copied!" : "Copy"}
</Button>
</div>
{copiedText && !isCopied && (
<p className="text-sm text-npui-muted-foreground">Last copied: "{copiedText}"</p>
)}
</div>
)
}