Copy Button

PreviousNext

Copy a value to the clipboard — an icon button, or a read-only field for API keys, resource ids and webhook URLs.

usr_8Kd2mPqR7x
https://api.matos-ui.com/v1/webhooks/9f3c2a
••••••••Q8sJ

Installation

pnpm dlx shadcn@latest add https://matos-ui.com/r/copy-button.json

Usage

import { CopyButton, CopyField } from '@/components/matos-ui/copy-button'
<CopyButton value="usr_8Kd2mPqR7xW3nL5vT" aria-label="Copy user ID" />

Examples

Labelled

<CopyButton value={apiKey} label="Copy" />

A value field

<CopyField value="https://api.example.com/v1/webhooks/9f3c2a" />

A secret

<CopyField secret value={liveKey} revealChars={4} />

The value is masked behind dots with a reveal toggle; the copy button always takes the whole value, revealed or not.

How it's built

  • The confirmation is a tier, not a colour. The clipboard glyph swaps to a check on spring.playful — the one tier the system keeps for a small thing that went right — and reverts on spring.fast after timeout (2s). Both glyphs share one grid cell, so nothing reflows during the swap.
  • It tells the truth. navigator.clipboard.writeText can fail — an insecure context, a denied permission — so the hook returns false and the button stays in its resting state rather than flashing "Copied". A visually-hidden aria-live region announces the real result.
  • CopyField is flush. A 1px border-input and no fill — it mirrors the input it looks like, so it sits on its substrate rather than floating.
  • hover-lift carries the 1px float and its press-back for every button here — the DESIGN §3.9 token, not a hand-rolled whileHover.
  • prefers-reduced-motion collapses every swap to a plain opacity crossfade; the check, the label and the reveal toggle still change, they just don't scale.

Reference

CopyButton Props

PropTypeDefaultDescription
valuestringThe string written to the clipboard.
labelReactNodeText beside the icon; swaps to "Copied" when active.
timeoutnumber2000How long the copied state shows, in ms.
onCopy(value: string) => voidFires after a successful copy.
size"sm" | "md" | "lg""md"Size preset.

CopyField Props

PropTypeDefaultDescription
valuestringThe full value — copied whole even while masked.
secretbooleanfalseMask the value and show a reveal toggle.
revealCharsnumber4Trailing characters left visible while masked.
timeoutnumber2000Copied-state duration, in ms.
onCopy(value: string) => voidFires after a successful copy.
size"sm" | "md""md"Size preset.