@usefy/use-copy-to-clipboard
v0.2.4
Published
A React hook for copying text to clipboard with fallback support
Maintainers
Readme
Overview
@usefy/use-copy-to-clipboard provides a simple way to copy text to the clipboard using the modern Clipboard API with automatic fallback for older browsers. Features include auto-reset timeout, success/error callbacks, and copy state tracking.
Part of the @usefy ecosystem — a collection of production-ready React hooks designed for modern applications.
Why use-copy-to-clipboard?
- Zero Dependencies — Pure React implementation with no external dependencies
- TypeScript First — Full type safety with exported interfaces
- Modern + Fallback — Uses Clipboard API with automatic
execCommandfallback - Auto Reset — Copied state automatically resets after configurable timeout
- Callbacks —
onSuccessandonErrorcallbacks for custom handling - Async/Await — Returns promise with boolean success indicator
- SSR Compatible — Works seamlessly with Next.js, Remix, and other SSR frameworks
- Stable References — Memoized copy function for optimal performance
- Well Tested — Comprehensive test coverage with Vitest
Installation
# npm
npm install @usefy/use-copy-to-clipboard
# yarn
yarn add @usefy/use-copy-to-clipboard
# pnpm
pnpm add @usefy/use-copy-to-clipboardPeer Dependencies
This package requires React 18 or 19:
{
"peerDependencies": {
"react": "^18.0.0 || ^19.0.0"
}
}Quick Start
import { useCopyToClipboard } from "@usefy/use-copy-to-clipboard";
function CopyButton() {
const [copiedText, copy] = useCopyToClipboard();
return (
<button onClick={() => copy("Hello World!")}>
{copiedText ? "Copied!" : "Copy"}
</button>
);
}API Reference
useCopyToClipboard(options?)
A hook that provides clipboard copy functionality with state tracking.
Parameters
| Parameter | Type | Description |
| --------- | --------------------------- | --------------------- |
| options | UseCopyToClipboardOptions | Configuration options |
Options
| Option | Type | Default | Description |
| ----------- | ------------------------ | ------- | --------------------------------------------------------------------- |
| timeout | number | 2000 | Time in ms before copiedText resets to null. Set to 0 to disable. |
| onSuccess | (text: string) => void | — | Callback called when copy succeeds |
| onError | (error: Error) => void | — | Callback called when copy fails |
Returns [copiedText, copy]
| Index | Type | Description |
| ----- | ------------------------------------ | -------------------------------------------- |
| [0] | string \| null | The last successfully copied text, or null |
| [1] | (text: string) => Promise<boolean> | Async function to copy text |
Examples
Basic Copy Button
import { useCopyToClipboard } from "@usefy/use-copy-to-clipboard";
function CopyButton({ text }: { text: string }) {
const [copiedText, copy] = useCopyToClipboard();
return (
<button onClick={() => copy(text)}>
{copiedText === text ? "Copied!" : "Copy to Clipboard"}
</button>
);
}Copy with Visual Feedback
import { useCopyToClipboard } from "@usefy/use-copy-to-clipboard";
function CopyWithIcon({ text }: { text: string }) {
const [copiedText, copy] = useCopyToClipboard();
const isCopied = copiedText === text;
return (
<button onClick={() => copy(text)} className={isCopied ? "copied" : ""}>
{isCopied ? (
<CheckIcon className="icon" />
) : (
<CopyIcon className="icon" />
)}
{isCopied ? "Copied!" : "Copy"}
</button>
);
}Code Block with Copy
import { useCopyToClipboard } from "@usefy/use-copy-to-clipboard";
function CodeBlock({ code, language }: { code: string; language: string }) {
const [copiedText, copy] = useCopyToClipboard();
return (
<div className="code-block">
<div className="code-header">
<span>{language}</span>
<button onClick={() => copy(code)}>
{copiedText === code ? "Copied!" : "Copy Code"}
</button>
</div>
<pre>
<code>{code}</code>
</pre>
</div>
);
}Custom Timeout
import { useCopyToClipboard } from "@usefy/use-copy-to-clipboard";
function LongFeedbackCopy() {
// Show "Copied!" for 5 seconds
const [copiedText, copy] = useCopyToClipboard({ timeout: 5000 });
return (
<button onClick={() => copy("Long feedback!")}>
{copiedText ? "Copied!" : "Copy"}
</button>
);
}Persistent Copied State
import { useCopyToClipboard } from "@usefy/use-copy-to-clipboard";
function PersistentCopy() {
// Never auto-reset the copied state
const [copiedText, copy] = useCopyToClipboard({ timeout: 0 });
return (
<div>
<button onClick={() => copy("Persistent!")}>
{copiedText ? "Copied!" : "Copy"}
</button>
{copiedText && <span>Copied text: {copiedText}</span>}
</div>
);
}With Callbacks
import { useCopyToClipboard } from "@usefy/use-copy-to-clipboard";
import { toast } from "your-toast-library";
function CopyWithToast({ text }: { text: string }) {
const [, copy] = useCopyToClipboard({
onSuccess: (copiedText) => {
toast.success(`Copied: ${copiedText}`);
},
onError: (error) => {
toast.error(`Failed to copy: ${error.message}`);
},
});
return <button onClick={() => copy(text)}>Copy</button>;
}Async Handling
import { useCopyToClipboard } from "@usefy/use-copy-to-clipboard";
function AsyncCopy({ text }: { text: string }) {
const [, copy] = useCopyToClipboard();
const [status, setStatus] = useState<"idle" | "success" | "error">("idle");
const handleCopy = async () => {
const success = await copy(text);
setStatus(success ? "success" : "error");
};
return (
<div>
<button onClick={handleCopy}>Copy</button>
{status === "success" && (
<span className="success">Copied successfully!</span>
)}
{status === "error" && <span className="error">Failed to copy</span>}
</div>
);
}Share URL Button
import { useCopyToClipboard } from "@usefy/use-copy-to-clipboard";
function ShareButton() {
const [copiedText, copy] = useCopyToClipboard();
const handleShare = () => {
copy(window.location.href);
};
return (
<button onClick={handleShare}>
{copiedText ? "Link Copied!" : "Share Link"}
</button>
);
}Copy Multiple Items
import { useCopyToClipboard } from "@usefy/use-copy-to-clipboard";
function CopyList({ items }: { items: string[] }) {
const [copiedText, copy] = useCopyToClipboard();
return (
<ul>
{items.map((item) => (
<li key={item}>
<span>{item}</span>
<button onClick={() => copy(item)}>
{copiedText === item ? "Copied!" : "Copy"}
</button>
</li>
))}
</ul>
);
}API Key Display
import { useCopyToClipboard } from "@usefy/use-copy-to-clipboard";
function ApiKeyDisplay({ apiKey }: { apiKey: string }) {
const [copiedText, copy] = useCopyToClipboard();
const maskedKey = `${apiKey.slice(0, 4)}${"*".repeat(20)}${apiKey.slice(-4)}`;
return (
<div className="api-key">
<code>{maskedKey}</code>
<button onClick={() => copy(apiKey)}>
{copiedText === apiKey ? "Copied!" : "Copy Key"}
</button>
</div>
);
}TypeScript
This hook is written in TypeScript with exported types.
import {
useCopyToClipboard,
type UseCopyToClipboardOptions,
type UseCopyToClipboardReturn,
type CopyFn,
} from "@usefy/use-copy-to-clipboard";
// Return type
const [copiedText, copy]: UseCopyToClipboardReturn = useCopyToClipboard();
// copiedText: string | null
// copy: (text: string) => Promise<boolean>
// Options type
const options: UseCopyToClipboardOptions = {
timeout: 3000,
onSuccess: (text) => console.log("Copied:", text),
onError: (error) => console.error("Error:", error),
};Browser Support
This hook uses the modern Clipboard API when available, with automatic fallback to document.execCommand('copy') for older browsers.
| Browser | Clipboard API | Fallback | | -------------- | ------------- | -------- | | Chrome 66+ | Yes | - | | Firefox 63+ | Yes | - | | Safari 13.1+ | Yes | - | | Edge 79+ | Yes | - | | IE 11 | No | Yes | | Older browsers | No | Yes |
Testing
This package maintains comprehensive test coverage to ensure reliability and stability.
Test Coverage
📊 View Detailed Coverage Report (GitHub Pages)
Test Categories
- Copy text successfully using Clipboard API
- Update copiedText after successful copy
- Return true on successful copy
- Handle empty string
- Handle special characters
- Reset copiedText after default timeout (2000ms)
- Reset after custom timeout
- Not reset when timeout is 0
- Reset timer on consecutive copies
- Use fallback when Clipboard API is not available
- Try fallback when Clipboard API throws error
License
MIT © mirunamu
This package is part of the usefy monorepo.
