DEVOLOGIST</>

React Hooks

useClipboard Hook in React

A reusable custom hook for copying text to the clipboard with modern API support, fallback handling, and simple error management.

Why use a clipboard hook?

Copying text is a common UI feature in modern applications. Instead of repeating clipboard logic in every component, a custom hook keeps the code reusable, clean, and easier to maintain.

What this hook does

This useClipboard hook detects browser support, copies text using the Clipboard API when available, falls back to execCommand, and exposes useful states like copied, error, and supported.

The Hook

Here is the full source code:

import { useCallback, useEffect, useState } from "react";
type UseClipboardReturn = {
copied: boolean;
copy: (text: string) => Promise<boolean>;
clear: () => void;
supported: boolean;
error: string | null;
};
export function useClipboard(timeout = 2000): UseClipboardReturn {
const [copied, setCopied] = useState(false);
const [error, setError] = useState<string | null>(null);
const [supported, setSupported] = useState(false);
useEffect(() => {
setSupported(
typeof navigator !== "undefined" &&
!!navigator.clipboard &&
typeof navigator.clipboard.writeText === "function"
);
}, []);
const clear = useCallback(() => {
setCopied(false);
setError(null);
}, []);
const copy = useCallback(
async (text: string): Promise<boolean> => {
try {
setError(null);
if (!supported) {
const textarea = document.createElement("textarea");
textarea.value = text;
textarea.style.position = "fixed";
textarea.style.opacity = "0";
textarea.style.left = "-9999px";
document.body.appendChild(textarea);
textarea.focus();
textarea.select();
const successful = document.execCommand("copy");
document.body.removeChild(textarea);
if (!successful) {
throw new Error("Copy failed");
}
} else {
await navigator.clipboard.writeText(text);
}
setCopied(true);
window.setTimeout(() => {
setCopied(false);
}, timeout);
return true;
} catch (err) {
setCopied(false);
setError(err instanceof Error ? err.message : "Clipboard copy failed");
return false;
}
},
[supported, timeout]
);
return {
copied,
copy,
clear,
supported,
error,
};
}

Usage Example

Use the hook in any component like this:

import React from "react";
import { useClipboard } from "./useClipboard";
export default function CopyButton() {
const { copy, copied, error, supported } = useClipboard(1500);
return (
<div className="flex flex-col gap-3">
<button
onClick={() => copy("Hello from React!")}
className="px-4 py-2 rounded bg-black text-white"
>
{copied ? "Copied!" : "Copy Text"}
</button>
{!supported && (
<p className="text-sm text-amber-600">
Your browser does not support Clipboard API.
</p>
)}
{error && <p className="text-sm text-red-600">{error}</p>}
</div>
);
}

Key Benefits

  • Reusable: Use the same clipboard logic anywhere in your app.
  • Compatible: Works with modern Clipboard API and fallback support.
  • User-Friendly: Provides copied state, error state, and support detection.

FAQ

Why use a custom clipboard hook in React?

A custom hook keeps your copy-to-clipboard logic reusable and avoids duplicating code in multiple components.

Does it support older browsers?

Yes. It uses the Clipboard API when available and falls back todocument.execCommand("copy").

Can I change the timeout?

Yes, pass a custom timeout value when calling useClipboard().