use-copy-to-clipboard
Hook for copying text to the clipboard with success/error callbacks and automatic reset timeout.
Demo
Status:Not Copied
Copy to clipboard hook
'use client'
import { useCopyToClipboard } from "@/hooks/use-copy-to-clipboard";
import { cn } from '@/lib/utils'
const CopyToClipboardDemo: React.FC = () => {
const {
copy,
isCopied,
copiedText,
} = useCopyToClipboard({ resetAfter: 3000 })
const handleCopyStaticText = () => {
copy('Hello, World!')
}
const handleCopyCode = () => {
const codeBlock = `
const { copy, isCopied } = useCopyToClipboard()
const handleCopy = () => {
copy('Hello, World!')
}`
copy(codeBlock)
}
return (
<div className='flex flex-col items-center justify-center gap-4 size-full'>
<div className="text-center flex flex-col gap-4 items-center">
<div className='flex gap-2 items-center justify-center'>
<span>Status:</span>
<span className={cn(isCopied && 'bg-green-500')}>{isCopied ? 'Copied' : 'Not Copied'}</span>
</div>
<div className="text-lg font-semibold text-foreground">
Copy to clipboard hook
</div>
<div className='flex gap-2 items-center'>
<button onClick={handleCopyStaticText}>Copy "Hello, World!"</button>
<button onClick={handleCopyCode}>Copy Code Block</button>
</div>
{copiedText && <span className='rounded-md mx-auto max-w-md p-4 mt-8'>
Copied: {copiedText}
</span>}
</div>
</div>
)
}
export { CopyToClipboardDemo };Installation
Copy and paste the following code into your project.
"use client";
import { useState, useCallback } from "react";
interface UseCopyToClipboardOptions {
/**
* Reset the success state after a certain number of milliseconds
* @default 2000
*/
resetAfter?: number;
}
interface UseCopyToClipboardReturn {
/**
* Whether the last copy operation was successful
*/
isCopied: boolean;
/**
* Whether the copy operation is in progress
*/
isCopying: boolean;
/**
* The last copied text
*/
copiedText: string | null;
/**
* Copy text to clipboard
*/
copy: (text: string) => Promise<boolean>;
}
/**
* A hook for copying text to the clipboard with success/error states
*
* @param options Configuration options for the hook
* @returns Object containing copy function and state
*
* @example
* ```tsx
* const { copy, isCopied, isCopying, copiedText, reset } = useCopyToClipboard({
* resetAfter: 3000,
* onSuccess: (text) => console.log('Copied:', text),
* onError: (error) => console.error('Copy failed:', error)
* })
*
* const handleCopy = () => {
* copy('Hello, World!')
* }
* ```
*/
export function useCopyToClipboard(
options: UseCopyToClipboardOptions = {}
): UseCopyToClipboardReturn {
const { resetAfter = 2000 } = options;
const [isCopied, setIsCopied] = useState(false);
const [isCopying, setIsCopying] = useState(false);
const [copiedText, setCopiedText] = useState<string | null>(null);
const copy = useCallback(
async (text: string): Promise<boolean> => {
if (!text) return false;
setIsCopying(true);
setIsCopied(false);
setCopiedText(null);
try {
// Check if the Clipboard API is available
if (navigator?.clipboard?.writeText) {
await navigator.clipboard.writeText(text);
} else {
// Fallback for older browsers
const textArea = document.createElement("textarea");
textArea.value = text;
textArea.style.position = "fixed";
textArea.style.left = "-999999px";
textArea.style.top = "-999999px";
document.body.appendChild(textArea);
textArea.focus();
textArea.select();
const successful = document.execCommand("copy");
document.body.removeChild(textArea);
if (!successful) {
throw new Error("Failed to copy text using fallback method");
}
}
setIsCopied(true);
setCopiedText(text);
// Auto-reset after specified time
if (resetAfter > 0) {
setTimeout(() => {
setIsCopied(false);
setCopiedText(null);
}, resetAfter);
}
return true;
} catch {
return false;
} finally {
setIsCopying(false);
}
},
[resetAfter]
);
return {
isCopied,
isCopying,
copiedText,
copy,
};
}Usage
import { useCopyToClipboard } from './use-copy-to-clipboard'
const {
copy,
isCopied,
copiedText
} = useCopyToClipboard({ resetAfter: 3000 })
copy('text').
.then(isCopied => {
console.log('Copied', isCopied)
})
.catch(error => {
console.error('Failed to copy', error)
})