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> ) }