Copy to Clipboard
FreeCopy button in inline, block and icon-only variants with success feedback, the async Clipboard API and an execCommand fallback for older browsers.
import { SvCopyToClipboardComponent } from '@styloviz/layout';Demo & usage
Basic usage
Usage example — open the gallery for the full interactive demo.
An interactive demo for this component lives in the gallery.
Open live demoMore examples
Icon-only button
<sv-copy-to-clipboard [text]="apiKey" variant="icon-only" size="sm" successLabel="Copied!" (copyError)="onCopyError($event)" />
A secret: masked, gated reveal, audited copy
The label is masked, revealing asks the host first, the success announcement warns, and every copy emits an SvSensitiveCopyEvent that never carries the value.
<sv-copy-to-clipboard variant="block" [text]="apiKey" [label]="apiKey" [sensitive]="true" sensitiveReveal="revealable" sensitiveField="apiKey" [revealAuthorized]="stepUpDone()" (revealRequest)="stepUp()" (copyAudited)="audit.log($event)" />
Why did it fail?
copyError emits an SvClipboardError whose reason is denied, insecure-context, unavailable or failed.
onCopyError(err: unknown) {
if (err instanceof SvClipboardError && err.reason === 'insecure-context') {
this.toast.show('Copying needs HTTPS — select the text and press Ctrl+C.');
}
}Drive it from a test (or a native shell)
providers: [{ provide: SV_CLIPBOARD, useValue: { writeText: () => Promise.reject(new SvClipboardError('denied')) } }]When to use
- Letting users copy a value (code snippet, API key, URL, token) in one click.
- You want built-in success feedback and a clipboard fallback for old browsers.
- For copying rich/multiple values, build a custom control around the clipboard API.
Async states
None — this component has nothing to load, so it has no loading, empty or error state to drive.
Inputs
| Name | Type | Default | Description |
|---|---|---|---|
| text | string | '' | Text to copy to clipboard. |
| successLabel | string | 'Copied!' | Label shown after successful copy. |
| successDuration | number | 2000 | Duration in ms before reverting to default state. |
| variant | CopyVariant | 'inline' | Display variant. |
| size | CopySize | 'md' | Component size. |
| label | string | '' | Placeholder shown when no text content is provided via ng-content. |
| ariaLabel | string | 'Copy to clipboard' | Accessible name of the copy button — the only name `icon-only` has. Name WHAT is copied ("Copy API key"), and localise it here. Never derived from `label`, which may be the (sensitive) value itself. |
| sensitive | boolean | false | Treat the value as a secret — an API key, a recovery code, a token. A COPIED SECRET LEAVES NO TRACE AND GETS NO WARNING from a generic copy button. On: the `label` is masked, the success announcement becomes `sensitiveWarning`, and every copy emits `copyAudited`. Off by default — the common use is a command or an id, and masking those would break them. |
| sensitiveReveal | CopySensitiveReveal | 'masked' | How a `sensitive` label is shown: `masked`, `revealable` (toggle) or `visible`. |
| sensitiveField | string | 'value' | Which field was copied, for the audit event's `field`. |
| sensitiveWarning | string | 'Copied. This value is secret — do not … | Announced (and shown in the block variant) after a sensitive copy. |
| requireAuthorization | boolean | true | Whether revealing a `sensitiveReveal="revealable"` value needs the host's authorization first. While on and `revealAuthorized` is off, the toggle emits `revealRequest` instead of revealing — step up, then set `revealAuthorized`. |
| revealAuthorized | boolean | false | The host has re-authenticated the user; the reveal toggle may reveal. |
| copyTimeout | number | SV_COPY_TIMEOUT_MS | How long (ms) a clipboard write may stay pending before it is abandoned as `failed` and `busy` clears. A write that never settles — a permission prompt left open, a native bridge that never answers — would otherwise leave the button busy forever. A late answer after the timeout is ignored. An invalid value (`0`, negative, `NaN`, `Infinity`) uses the default. |
Outputs
| Event | Payload type | Description |
|---|---|---|
| copied | void | Emitted after text is successfully copied. |
| copyError | unknown | Emitted when copying fails (both the Clipboard API and the fallback), or when the write did not settle within `copyTimeout`. The payload is an `SvClipboardError`: `reason` classifies it — `denied`, `insecure-context`, `unavailable` or `failed` (also used for a timeout). When both paths ran and failed, `cause` (and so `message`) is the FALLBACK's error — the last one thrown — while `reason` is derived from the Clipboard API's rejection. Typed `unknown` for compatibility; narrow with `instanceof SvClipboardError`. |
| copyAudited | SvSensitiveCopyEvent | A `sensitive` value reached the clipboard. The event says who, which field and when — never the value itself. Log it. |
| revealRequest | void | The user asked to reveal while `requireAuthorization` was on and `revealAuthorized` off. |
Customization & theming
- Pass the `text` to copy and choose a `variant` (inline, block, icon-only) and `size`.
- Customize the `label` and the post-copy `successLabel` / `successDuration`.
- Listen to `copied` for success and `copyError` for failures, to trigger toasts or analytics.
- Sensitive values (`sensitive`, `sensitiveReveal`, `sensitiveField`, `sensitiveWarning`, `requireAuthorization` / `revealAuthorized` / `revealRequest`, `copyAudited`) follow the kit-wide sensitive-value policy, and `provideSvSensitivePrivacy()` re-masks the label while the screen is shared.
- The clipboard sits behind the `SV_CLIPBOARD` injection token (default `svBrowserClipboard`): provide your own to test denied permission or an insecure origin, or to target a native shell.
- Variant axes: `variant` and `size` are appearance; `sensitive` + `sensitiveReveal` are a behavioural mode that works with every variant, except that `icon-only` has no label to mask or reveal, so it only warns and audits.
- Busy state: a click while a copy is in flight is ignored and the button reports `aria-busy="true"`; the success timer is cleared when the component is destroyed, and a copy that settles after destruction emits nothing. A write that never settles is abandoned after `copyTimeout` (default 10 000 ms, `SV_COPY_TIMEOUT_MS`): `copyError` fires with reason `failed`, busy clears, and a late answer is ignored.
- Layout (X-9): not applicable — the button and label sit in normal flow; nothing is portalled, measured or scrolled, and no containing block or breakpoint matters. The execCommand fallback appends an off-screen `position: fixed` textarea to `<body>` for the duration of the copy only, and removes it whether the copy succeeded or not.
- Browser limits: the async Clipboard API needs a secure context (HTTPS or localhost) and a user gesture, and an iframe needs the `clipboard-write` permission policy. Elsewhere the component falls back to the deprecated `execCommand('copy')`, which iOS Safari only honours inside the tap handler and which browsers may remove. When both fail, `copyError` carries `reason` (`denied`, `insecure-context`, `unavailable`, `failed`) and the live region tells the user to select the text and copy it manually.
Best practices
- Do: Confirm the copy visibly (`successLabel`) so users know it worked.
- Do: Give icon-only variants an accessible name with `ariaLabel` that says what is copied ("Copy API key") — `label` is visible text, and may be the value itself.
- Tip: Use the `copied` event to fire a toast or analytics event.
- Do: Set `sensitive` for API keys, recovery codes and tokens: the label masks, the copy warns, and `copyAudited` gives you an audit trail that never contains the secret.
- Don't: Don't project a secret through `<ng-content>` in sensitive mode — projected markup is yours and is not masked. Pass it through `label`.
Accessibility
- Renders a labelled button with an accessible name even in icon-only mode.
- Success and failure are announced via a polite live region, not colour-only; a failure names its reason and the manual workaround.
- Falls back to execCommand when the Clipboard API is unavailable, and emits `copyError` if both fail.
- In sensitive mode the reveal toggle is a real button with `aria-pressed`, and the success announcement is the `sensitiveWarning`.
- Focus stays on the copy button: the fallback's scratch textarea hands focus back after it copies.
Feedback on Copy to Clipboard
Spotted a gap in this page, or something that didn't work as described? Comments are GitHub Discussions in the public styloviz/community repo, so posting needs a GitHub account.