Skip to content

Components · Actions

CopyButton

Copies a value to the clipboard, confirms it visibly and to screen readers, and reports failures.

@rookcom/monarch-reactComponent

Labels and icon-only

Usage examples

Reporting a failure

useCopyToClipboard

Purpose

One button that copies a value (a link, a key, a DNS record), shows a short "Copied" state, announces the result in a polite live region and hands errors to the caller. useCopyToClipboard is the same logic for custom controls.

Use when

  • The user needs a value elsewhere: an invite link, a webhook URL, an ID, a code snippet.
  • You are building your own copy control: use useCopyToClipboard so the success, error and reset behaviour match.

Avoid when

  • The value should also be visible and selectable: use CopyField.
  • The value is a secret: use SecretField with copyable, which copies without revealing it.

Design principles

  • Feedback is visible and spoken: the icon becomes a check and the label "Copied", and a polite live region says "Copied to clipboard".
  • Never fails silently. The Clipboard API is tried first, then the legacy execCommand path; if both fail the button shows "Couldn't copy", announces it, and calls onCopyError.
  • The button keeps its width: all three labels share one grid cell, so nothing jumps when the state changes.
  • Built on Button, so variants, sizes, focus and disabled behaviour are the same as every other action.

API reference

import { CopyButton, useCopyToClipboard, writeToClipboard } from '@rookcom/monarch-react';
ExportKindRenderingRoot class
CopyButtoncomponentclient.mn-copy-button
useCopyToClipboardhookclient–
writeToClipboardutilityclient–

CopyButton forwards its ref to the <button>. It renders a visually hidden role="status" sibling for announcements.

useCopyToClipboard({ resetAfter, onCopy, onError }) returns { copy, status, error, reset }. copy(value) resolves true or false and never rejects.

writeToClipboard(text) is the low-level write (Clipboard API, then the legacy fallback); it rejects with an Error when both fail.

Generated from the TypeScript declarations of packages/react/src/components/actions/copy-button.

Properties

All Button props except children, asChild and leadingIcon pass through, including variant and size.

CopyButton

Copies a value to the clipboard, shows a short "Copied" state and announces the result through a polite live region. Errors are reported to the caller through `onCopyError`; the button never fails silently.

PropTypeDescription
value requiredCopyValueThe text to copy, or a function that produces it when clicked.
blockbooleanStretch to the container width.
brandColorstringBrand or tenant colour for `variant="brand"`, as `#rrggbb` or `#rgb`. The fill and text are derived per theme by the tokens contrast engine, so the label always meets WCAG AA (7:1 in more-contrast themes); the colour may be darkened or lightened to get there. Ignored by other variants. An invalid or missing colour falls back to the product accent.
classNamestring
copiedLabelstringLabel while in the copied state. Default "Copied".
copiedMessagestringPolite announcement after a successful copy. Default "Copied to clipboard".
disabledboolean
errorLabelstringLabel after a failed copy. Default "Couldn't copy".
errorMessagestringPolite announcement after a failure. Default "Couldn't copy. Select the text and copy it manually."
iconReactNodeReplaces the copy icon. Decorative.
iconOnlybooleanHide the visible text and show only the icon. `label` stays the accessible name, so give it the object: "Copy API key".
idstring
labelstringVisible label, and the accessible name. Default "Copy".
loadingbooleanShows a spinner, sets aria-busy and blocks clicks while keeping focus.
onCopy((text: string) => void)Called with the copied text after a successful copy.
onCopyError((error: Error) => void)Called when copying fails. Use it to show a toast or a manual-copy fallback.
resetAfternumberMilliseconds before the button returns to its idle look. Default 2000.
sizeButtonSize
targetstringWhat is copied, for screen readers ("webhook URL"). Appended to the visible label as visually hidden text, so several copy buttons on one page have distinct names: "Copy webhook URL", "Copied webhook URL".
trailingIconReactNode
variantButtonVariantVisual weight. Use one `primary` per view region. Default `secondary`.

Variants

PropValuesDescription
variant'primary''secondary''ghost''danger''danger-subtle'From Button. Default secondary, or ghost when iconOnly.
size'xs''sm''md''lg'From Button. Default md.
iconOnly'true''false'Square, icon-only button (data-icon-only). label stays the accessible name and tooltip.

States

StateExposed asBehaviour
Idledata-state="idle"Copy icon and label.
Copieddata-state="copied"Check icon in the success colour and copiedLabel, for resetAfter milliseconds (default 2000).
Errordata-state="error"Warning icon in the danger colour and errorLabel; onCopyError has been called.
DisableddisabledFrom Button: nothing is copied.

Interaction behaviour

  • Click, Enter or Space copies value. A function value is called at that moment and may be async.
  • Your onClick runs first; call event.preventDefault() in it to skip the copy.
  • Each attempt clears and refills the live region, so copying twice is announced twice.
  • The state returns to idle after resetAfter; 0 keeps it until the next attempt.

Keyboard support

KeysAction
TabMoves focus to the button.
Enter / SpaceCopies the value.

Accessibility

Role
button
Accessible name
The visible label, plus the visually hidden target ("Copy" + "invite link"). Icon-only: label (+ target) as aria-label.
  • The result is announced through a role="status" live region outside the button, so it is not part of the button name.
  • Icon-only buttons keep a stable aria-label; the change is announced instead of renamed.
  • Use target when several copy buttons share a screen, so each has a distinct name (WCAG 2.4.6). The visible text stays at the start of the name (WCAG 2.5.3).
  • Icons are decorative (aria-hidden).

Responsive behaviour

  • Same height as Button at every width (--mn-control-*).
  • The label never wraps; use iconOnly in tight rows such as table cells.

Theme support

  • Colours come from Button and the success and danger tokens, so light, dark and more-contrast themes apply automatically.
  • On primary and danger buttons the state icon inherits the on-solid text colour.
  • The icon colour transition is removed under prefers-reduced-motion: reduce.

Do and don't

Do

  • Name what is copied: target="API key" or label="Copy invite link".
  • Handle onCopyError: show a toast or point the user at a selectable copy of the value.
  • Use CopyField when the user should also see the value.

Don't

  • Do not show a toast as the only confirmation; the button already confirms visibly and to screen readers.
  • Do not put a secret in a plain CopyButton next to its plain text; use SecretField.
  • Do not change label while the button is showing "Copied"; use copiedLabel.
  • CopyField: Shows the value, read-only, with this button beside it.
  • SecretField: Copies a masked secret without revealing it.
  • Button: The control CopyButton is built on.
  • Toast: A product-level way to report onCopyError.

Dependencies

KindDepends on
npm packagesreact
Monarch componentsButton

Testing coverage

  • Copies the value, shows the copied state, and announces "Copied to clipboard".
  • Copies with Enter and Space.
  • A refused write calls onCopyError, shows the error state and announces it.
  • Falls back to execCommand when the Clipboard API rejects; rejects when nothing is available.
  • Returns to idle after resetAfter.
  • Accepts sync and async function values.
  • Icon-only keeps a stable accessible name.
  • Does not copy when disabled or when onClick prevents default.
  • Inactive labels are hidden from the accessible name.
  • useCopyToClipboard exposes status, error and reset.
CheckCovered
Axe in jsdomYes
Server renderingYes
Browser suitesVisual baselines: /__gallery; Cross-browser axe: /components/copy-button

Tests: packages/react/src/components/actions/copy-button