Mischief

62 / Controls

Copy Button

Copies a value and says whether it worked, including when the clipboard refuses.

sk_live_7f3a91c2b8e04d5aa1

Not copied yet

Installation

Copy the source into your project, or keep it behind a package.

npx shadcn@latest add Tinkerers-Labs/mischief-ui/copy-button
import { CopyButton } from "mischief-ui/copy-button"
Also installs
  • lucide-react

Or paste it in yourself. The source imports the shared cn helper from @/lib/utils, so point that at your own copy.

registry/default/copy-button/copy-button.tsx
"use client" import * as React from "react"import { Check, Clipboard } from "lucide-react" import { cn } from "@/lib/utils" export type CopyButtonProps = Omit<  React.ButtonHTMLAttributes<HTMLButtonElement>,  "children" | "value"> & {  value: string  /** Shown beside the icon. Omit for an icon-only control. */  children?: React.ReactNode

Usage

export function Key({ apiKey }) {
  return (
    <CopyButton value={apiKey} onCopied={() => track("key_copied")}>
      Copy key
    </CopyButton>
  )
}

When the clipboard refuses

navigator.clipboard.writeText rejects more often than it looks: a page served over http, a sandboxed frame, a browser that wants a user gesture it did not see, a permission that was denied. Left unhandled the button does nothing, says nothing, and leaves an unhandled rejection behind.

This one catches it, says so, and tells you through onCopyError. Reach for that when there is somewhere better to put the failure -- a toast, or a field the reader can select from by hand.

API

valuestringWhat lands on the clipboard.
childrenReactNodeShown beside the icon. Omit for icon only.
label, copiedLabel, errorLabelstringThe three things it can say.
onCopied(value: string) => voidCalled after a copy that worked.
onCopyError(error: unknown) => voidCalled when the clipboard refused.

Accessibility

The outcome is announced through a polite live region whether or not the label is visible, so an icon-only button is not silent. With no visible text it takes the current wording as its accessible name, which changes to say what happened. Copying is one press, and nothing about it depends on hovering.