Getting Started
Blocks
useCopyToClipboard
A React hook for copying text to the clipboard with fallback support and feedback state management.
About
The useCopyToClipboard hook provides a simple and reliable way to copy text to the clipboard with automatic fallback support and user feedback state management.
Features
- ✅ Cross-browser Compatibility: Uses modern Clipboard API when available, falls back to
document.execCommand
for older browsers - ✅ User Feedback: Built-in
isCopied
state for showing copy confirmation - ✅ Customizable Delay: Configure how long the "copied" state persists
- ✅ Safe Implementation: Handles errors gracefully and cleans up temporary elements
- ✅ Zero Dependencies: No external dependencies required
- ✅ TypeScript Support: Fully typed with excellent developer experience
Preview
Basic Copy
Copy Code
const greeting = "Hello, World!";
console.log(greeting);
"use client"
import { Check, Copy } from "lucide-react"
import { Button } from "@/components/ui/button"
import { useCopyToClipboard } from "@/hooks/use-copy-to-clipboard"
export function UseCopyToClipboardDemo() {
const { isCopied, copyToClipboard } = useCopyToClipboard()
const handleCopyText = () => {
copyToClipboard(
"Hello, World! This text has been copied to your clipboard."
)
}
const handleCopyCode = () => {
const code = `const greeting = "Hello, World!";
console.log(greeting);`
copyToClipboard(code)
}
return (
<div className="space-y-4 p-4">
<div className="space-y-2">
<h3 className="text-sm font-medium">Basic Copy</h3>
<Button
onClick={handleCopyText}
variant={isCopied ? "default" : "outline"}
>
{isCopied ? (
<>
<Check className="mr-2 h-4 w-4" />
Copied!
</>
) : (
<>
<Copy className="mr-2 h-4 w-4" />
Copy Text
</>
)}
</Button>
</div>
<div className="space-y-2">
<h3 className="text-sm font-medium">Copy Code</h3>
<div className="relative">
<pre className="bg-muted overflow-x-auto rounded-md p-3 text-sm">
<code>{`const greeting = "Hello, World!";
console.log(greeting);`}</code>
</pre>
<Button
size="sm"
variant="ghost"
onClick={handleCopyCode}
className="absolute top-2 right-2 h-8 w-8 p-0"
>
{isCopied ? (
<Check className="h-3 w-3" />
) : (
<Copy className="h-3 w-3" />
)}
</Button>
</div>
</div>
</div>
)
}
Installation
pnpm dlx shadcn@latest add https://ui.nowts.app/r/use-copy-to-clipboard.json
Usage
Basic copy button
import { useCopyToClipboard } from "@/hooks/use-copy-to-clipboard"
import { Button } from "@/components/ui/button"
export function BasicCopyButton() {
const { isCopied, copyToClipboard } = useCopyToClipboard()
return (
<Button
onClick={() => copyToClipboard("Hello, World!")}
variant={isCopied ? "default" : "outline"}
>
{isCopied ? "Copied!" : "Copy Text"}
</Button>
)
}
Copy with custom delay
import { useCopyToClipboard } from "@/hooks/use-copy-to-clipboard"
import { Button } from "@/components/ui/button"
export function CustomDelayExample() {
// Reset feedback state after 3 seconds
const { isCopied, copyToClipboard } = useCopyToClipboard(3000)
return (
<Button onClick={() => copyToClipboard("Text to copy")}>
{isCopied ? "Copied!" : "Copy"}
</Button>
)
}
Copy code block with icon
import { Check, Copy } from "lucide-react"
import { useCopyToClipboard } from "@/hooks/use-copy-to-clipboard"
import { Button } from "@/components/ui/button"
export function CopyCodeBlock() {
const { isCopied, copyToClipboard } = useCopyToClipboard()
const code = `const greeting = "Hello, World!";
console.log(greeting);`
return (
<div className="relative">
<pre className="bg-muted overflow-x-auto rounded-md p-4">
<code>{code}</code>
</pre>
<Button
size="sm"
variant="ghost"
onClick={() => copyToClipboard(code)}
className="absolute top-2 right-2 h-8 w-8 p-0"
>
{isCopied ? (
<Check className="h-4 w-4 text-green-600" />
) : (
<Copy className="h-4 w-4" />
)}
</Button>
</div>
)
}
Copy URL or sharing
import { Check, Share2 } from "lucide-react"
import { useCopyToClipboard } from "@/hooks/use-copy-to-clipboard"
import { Button } from "@/components/ui/button"
export function ShareButton() {
const { isCopied, copyToClipboard } = useCopyToClipboard(2000)
const handleShare = () => {
const url = window.location.href
copyToClipboard(url)
}
return (
<Button onClick={handleShare} variant="outline" size="sm">
{isCopied ? (
<>
<Check className="mr-2 h-4 w-4" />
Link Copied!
</>
) : (
<>
<Share2 className="mr-2 h-4 w-4" />
Share Link
</>
)}
</Button>
)
}
Copy form data
import { useState } from "react"
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"
export function CopyFormData() {
const { isCopied, copyToClipboard } = useCopyToClipboard()
const [email, setEmail] = useState("user@example.com")
const [apiKey, setApiKey] = useState("sk-1234567890abcdef")
const copyCredentials = () => {
const credentials = `Email: ${email}
API Key: ${apiKey}
Environment: Production`
copyToClipboard(credentials)
}
return (
<div className="space-y-4 rounded-lg border p-4">
<div className="space-y-2">
<Label htmlFor="email">Email</Label>
<Input
id="email"
value={email}
onChange={(e) => setEmail(e.target.value)}
readOnly
/>
</div>
<div className="space-y-2">
<Label htmlFor="api-key">API Key</Label>
<Input
id="api-key"
type="password"
value={apiKey}
onChange={(e) => setApiKey(e.target.value)}
readOnly
/>
</div>
<Button onClick={copyCredentials} className="w-full">
{isCopied ? "Credentials Copied!" : "Copy Credentials"}
</Button>
</div>
)
}
Copy with toast notification
import { useEffect } from "react"
import { toast } from "sonner"
import { useCopyToClipboard } from "@/hooks/use-copy-to-clipboard"
import { Button } from "@/components/ui/button"
export function CopyWithToast() {
const { isCopied, copyToClipboard } = useCopyToClipboard()
useEffect(() => {
if (isCopied) {
toast.success("Text copied to clipboard!")
}
}, [isCopied])
const handleCopy = () => {
copyToClipboard("This text will show a toast notification when copied!")
}
return <Button onClick={handleCopy}>Copy with Toast</Button>
}
API
Parameters
Parameter | Type | Default | Description |
---|---|---|---|
delay | number | 5000 | Duration in milliseconds to keep the isCopied state as true |
Returns
Property | Type | Description |
---|---|---|
isCopied | boolean | Whether text was recently copied (resets after delay) |
copyToClipboard | (text: string) => void | Function to copy text to clipboard |
Browser Support
The hook automatically handles browser compatibility:
- Modern browsers: Uses the Clipboard API (
navigator.clipboard.writeText
) - Older browsers: Falls back to
document.execCommand('copy')
with temporary textarea - Error handling: Logs errors to console and continues gracefully
TypeScript
The hook is fully typed and provides excellent IntelliSense support:
// The hook is typed like this:
declare const useCopyToClipboard: (delay?: number) => {
isCopied: boolean
copyToClipboard: (text: string) => void
}
// Usage with TypeScript
const { isCopied, copyToClipboard } = useCopyToClipboard(3000)
// isCopied is inferred as boolean
// copyToClipboard is inferred as (text: string) => void
Build Your SaaS in Days, Not Months
NOW.TS is the Next.js 15 boilerplate with everything you need to launch your SaaS—auth, payments, database, and AI-ready infrastructure.
Learn more about NOW.TS