Use Copy To Clipboard
Copy text with a self-resetting copied flag — pairs with iconSwapVariants for the Copy ↔ Check affordance.
bash
pnpm add @aur-ui/uitsx
import { useCopyToClipboard } from "@aur-ui/ui/hooks/use-copy-to-clipboard";
function CopyValue({ value }: { value: string }) {
const { copied, copy } = useCopyToClipboard();
return (
<button type="button" onClick={() => void copy(value)}>
{copied ? <CheckIcon /> : <CopyIcon />}
</button>
);
}copy resolves to whether the write succeeded (missing clipboard API or a
denied permission returns false, it never throws). The copied flag resets
after 1.5s by default — pass useCopyToClipboard(ms) to change it; rapid
re-copies restart the timer, and unmount clears it.
hooks/use-copy-to-clipboard.ts
"use client";
import { useCallback, useEffect, useRef, useState } from "react";
/**
* Copy-to-clipboard with a self-resetting `copied` flag — pairs with
* `iconSwapVariants` for the Copy ↔ Check affordance.
*
* `copy` resolves to whether the write succeeded (clipboard API missing or
* permission denied → `false`, never throws). The flag resets after
* `resetDelay` ms; rapid re-copies restart the timer, and unmount clears it.
*/
export function useCopyToClipboard(resetDelay = 1500): {
copied: boolean;
copy: (text: string) => Promise<boolean>;
} {
const [copied, setCopied] = useState(false);
const timerRef = useRef<ReturnType<typeof setTimeout> | undefined>(undefined);
useEffect(() => () => clearTimeout(timerRef.current), []);
const copy = useCallback(
async (text: string) => {
if (!navigator.clipboard) return false;
try {
await navigator.clipboard.writeText(text);
} catch {
return false;
}
setCopied(true);
clearTimeout(timerRef.current);
timerRef.current = setTimeout(() => setCopied(false), resetDelay);
return true;
},
[resetDelay],
);
return { copied, copy };
}