Copy Button
A button that copies text to the clipboard.
npx shadcn@latest add @shuip/copy-button
pnpm dlx shadcn@latest add @shuip/copy-button
bun x shadcn@latest add @shuip/copy-button
'use client';
import { CheckIcon, Copy } from 'lucide-react';
import * as React from 'react';
import { Button } from '@/components/ui/button';
const DEFAULT_TIMEOUT = 2000;
export interface CopyButtonProps extends React.ComponentProps<typeof Button> {
value: string;
copiedIcon?: React.ReactNode;
notCopiedIcon?: React.ReactNode;
}
export function CopyButton({ value, copiedIcon = <CheckIcon />, notCopiedIcon = <Copy />, ...props }: CopyButtonProps) {
const [hasCopied, setHasCopied] = React.useState(false);
const handleCopy = () => {
navigator.clipboard.writeText(value);
setHasCopied(true);
setTimeout(() => {
setHasCopied(false);
}, DEFAULT_TIMEOUT);
};
return (
<Button size='icon' variant={'ghost'} className={'z-10 size-4'} onClick={handleCopy} {...props}>
<span className='sr-only'>Copy</span>
{hasCopied ? copiedIcon : notCopiedIcon}
</Button>
);
}
The command is the same for Base UI and Radix: the CLI reads the style in your components.json and installs the matching variant.
Examples
Default
With Custom Icons
'use client';
import { Cat, Dog } from 'lucide-react';
import { CopyButton } from '@/components/ui/shuip/copy-button';
export default function CopyButtonWithCustomIconsExample() {
return (
<CopyButton
value='Hello, cat!'
copiedIcon={<Dog className='size-6' />}
notCopiedIcon={<Cat className='size-6' />}
className='size-8'
variant={'default'}
/>
);
}
Props
Prop
Type