The useCopier hook provides robust clipboard copy functionality with success state management, error handling, and customizable callbacks. It's designed for code blocks, buttons, and interactive elements that need copy-to-clipboard functionality.
Note
When using
CodeHighlighter, theuseCodeanduseDemohooks already provide built-in copy functionality, souseCopieris not needed.
A simple example showing how to copy text from an input field.
'use client';
import * as React from 'react';
import { useCopier } from '@fairgarden/docs/useCopier';
import styles from './TextInputCopy.module.css';
export function TextInputCopy() {
const [text, setText] = React.useState('Hello, copy me!');
const { copy, recentlySuccessful } = useCopier(() => text);
return (
<div className={styles.container}>
<input
type="text"
value={text}
onChange={(event) => setText(event.target.value)}
className={styles.input}
placeholder="Enter text to copy…"
aria-label="Text to copy"
/>
<button type="button" onClick={copy} className={styles.button}>
{recentlySuccessful ? '✓ Copied!' : 'Copy'}
</button>
</div>
);
}
import { useCopier } from '@fairgarden/docs/useCopier';
function CodeBlockCopy({ getCode }: { getCode: () => string }) {
const { copy, recentlySuccessful } = useCopier(getCode);
return (
<button onClick={copy} disabled={recentlySuccessful}>
{recentlySuccessful ? '✓ Copied' : 'Copy Code'}
</button>
);
}
import { useCopier } from '@fairgarden/docs/useCopier';
function CopyWithFeedback({ content }: { content: string }) {
const [error, setError] = React.useState<string | null>(null);
const { copy, recentlySuccessful } = useCopier(content, {
onCopied: () => setError(null),
onError: (err) => setError('Failed to copy to clipboard'),
timeout: 3000,
});
return (
<div>
<button onClick={copy}>{recentlySuccessful ? 'Copied!' : 'Copy'}</button>
{error && <span style={{ color: 'red' }}>{error}</span>}
</div>
);
}
import { useCopier } from '@fairgarden/docs/useCopier';
function AnalyticsCopyButton({ text, label }: { text: string; label: string }) {
const { copy, recentlySuccessful } = useCopier(text, {
onCopied: () => {
// Track successful copy events
analytics.track('Code Copied', { label });
},
onError: (error) => {
// Track copy failures
analytics.track('Copy Failed', { label, error: String(error) });
},
onClick: (event) => {
// Track all copy attempts
analytics.track('Copy Attempted', { label });
},
});
return (
<button onClick={copy} className={recentlySuccessful ? 'success' : ''}>
{recentlySuccessful ? 'Copied!' : `Copy ${label}`}
</button>
);
}
| Parameter | Type | Description |
|---|---|---|
| contents | | |
| opts | |
| Key | Type | Required |
|---|---|---|
| copy | | Yes |
| recentlySuccessful | | Yes |
type UseCopierOpts = {
onCopied?: OnCopied;
onError?: OnCopyError;
onClick?: OnCopyClick;
timeout?: number;
}copy is called, resolves content from string or functionclipboard-copy library for cross-browser compatibilityrecentlySuccessful to true after successful copyThe hook manages internal state for copy feedback:
recentlySuccessful is falsetrue and the timeout countdown (re)startsfalsetrue while the new write is in flight, so feedback keyed on it doesn't flicker; each successful write restarts the countdown, so the state stays true until timeout after the last write to finish. A write still in flight timeout after it started ends the feedback, so a stalled write can't keep it on. Use onCopied to react to every copy.false and the error callback is firedfalse; neither onCopied nor onError is fired, but onClick still isCopy operations can fail for various reasons:
The hook provides the onError callback to handle these cases gracefully.
Empty content (an empty string, or a function that returns undefined) is not an error: there is nothing to copy, so the clipboard is left untouched and recentlySuccessful stays false.
Uses the clipboard-copy library which provides:
navigator.clipboard.writeText() API when availabledocument.execCommand('copy') for older browsersCodeHighlighter - Often used together for code copy functionality