Components
Loading preview...
Copy text to the clipboard easily with useCopyToClipboard, while keeping track of the most recently copied value.
@strlrd-29
npx shadcn@latest add https://21st.dev/r/strlrd-29/use-copy-to-clipboard'use client'
import { useCopyToClipboard } from "@/components/hooks/use-copy-to-clipboard"
import { Button } from "@/components/ui/button"
import { Card } from "@/components/ui/card"
import { Check, Copy, ClipboardX } from "lucide-react"
import { useEffect, useState } from "react"
import { toast } from "sonner"
const SAMPLE_TEXTS = [
{ label: 'Short Text', content: 'Hello, World!' },
{ label: 'URL', content: 'https://example.com/very/long/url/path' },
{ label: 'Code Snippet', content: 'const greeting = "Hello, World!";' },
]
export function UseCopyToClipboardDemo() {
const [copiedText, copy] = useCopyToClipboard()
const [lastCopiedId, setLastCopiedId] = useState<number | null>(null)
const handleCopy = async (text: string, id: number) => {
try {
const success = await copy(text)
if (success) {
setLastCopiedId(id)
toast.success('Текст скопирован в буфер обмена')
// Reset copied state after 2 seconds
setTimeout(() => {
setLastCopiedId(null)
}, 2000)
} else {
toast.error('Не удалось скопировать текст')
}
} catch (error) {
toast.error('Произошла ошибка при копировании')
console.error('Copy failed:', error)
}
}
// Reset copied state when component unmounts
useEffect(() => {
return () => setLastCopiedId(null)
}, [])
return (
<Card className="p-6 max-w-2xl mx-auto">
<div className="space-y-6">
{/* Header */}
<div className="space-y-2">
<h3 className="text-lg font-medium">Copy to Clipboard Demo</h3>
<p className="text-sm text-muted-foreground">
Click on any item below to copy its content to clipboard
</p>
</div>
{/* Copy Items */}
<div className="grid gap-4">
{SAMPLE_TEXTS.map((item, index) => (
<div
key={index}
className="flex items-center justify-between p-4 rounded-lg border bg-card"
>
<div className="space-y-1 flex-1 mr-4">
<p className="text-sm font-medium">{item.label}</p>
<code className="text-xs bg-muted p-1 rounded">
{item.content}
</code>
</div>
<Button
variant="outline"
size="icon"
onClick={() => handleCopy(item.content, index)}
className="h-8 w-8"
>
{lastCopiedId === index ? (
<Check className="h-4 w-4 text-green-500" />
) : (
<Copy className="h-4 w-4" />
)}
</Button>
</div>
))}
</div>
{/* Status Display */}
<div className="flex items-center gap-2 p-3 rounded-lg bg-muted">
<div className="text-sm">Last copied value:</div>
{copiedText ? (
<code className="text-xs bg-background px-2 py-1 rounded flex-1">
{copiedText}
</code>
) : (
<span className="text-sm text-muted-foreground flex items-center gap-2">
<ClipboardX className="h-4 w-4" />
Nothing copied yet
</span>
)}
</div>
{/* Documentation */}
<div className="text-sm text-muted-foreground border-t pt-4">
<p className="font-medium mb-2">Hook Usage:</p>
<pre className="bg-muted p-3 rounded-md text-xs">
{`const [copiedText, copy] = useCopyToClipboard()
// Copy text
await copy("Text to copy")
// Check last copied text
console.log(copiedText) // null if nothing copied`}
</pre>
</div>
</div>
</Card>
)
}