Skip to main content
Copy for LLM

Clipboard Button

The <x-wirekit::clipboard-button> component copies a value to the clipboard on click and temporarily shows a "Copied!" confirmation. It uses the Clipboard API and announces the copy action to screen readers via a live region.

Usage

Clipboard Button

Inline with a code snippet:

<x-wirekit::row justify="between" style="background: var(--color-wk-bg-muted); border-radius: var(--radius-wk-lg); padding: var(--padding-wk-y-sm) var(--padding-wk-x-md);">
    <code style="font-family: var(--font-wk-mono); font-size: var(--text-wk-sm);">composer require pushery/wirekit</code>
    <x-wirekit::clipboard-button value="composer require pushery/wirekit">
        Copy
    </x-wirekit::clipboard-button>
</x-wirekit::row>

Custom Confirmation Text

Change the text shown after copying:

Custom Confirmation Text

Custom Duration

Control how long the "Copied!" state persists (in milliseconds):

Custom Duration (5s)

Copying Dynamic Values

Use Blade expressions to copy dynamic content:

<x-wirekit::clipboard-button :value="$user->api_key">
    Copy API Key
</x-wirekit::clipboard-button>

Code Block with Copy

Combine with other components to create a copyable code snippet:

Code Block with Copy
composer require pushery/wirekit

Visual Behavior

  1. Default state — shows a clipboard icon + your slot content (e.g., "Copy Link")
  2. After click — icon swaps to a green checkmark, label changes to copiedText
  3. After duration — reverts to the default state automatically

The icon swap is handled by Alpine's x-show directives. The checkmark icon uses --color-wk-success-text for the green color.

When the browser refuses the write, the button takes the confirmation back and shows the refusal: a warning icon replaces the check, and a labeled button reads Copy failed in place of its label, both for the same duration. A screen reader hears the refusal as an alert.

Requirements

The clipboard button uses the Clipboard API (navigator.clipboard.writeText()), which requires:

  • HTTPS or localhost — the Clipboard API is only available in secure contexts
  • User gesture — the copy happens inside a click handler, which satisfies the browser's user activation requirement

If neither condition is met, the copy fails, and the button shows it the way it shows a refused write — see Visual Behavior. All modern browsers (Chrome, Firefox, Safari, Edge) support the Clipboard API.

Props

Prop Type Default Description
value string '' The text to copy to the clipboard
copiedText string 'Copied' Label shown temporarily after copying. Translated through the wirekit::Copied catalog key, so overriding it in lang/vendor/wirekit/ reaches every locale at once
duration int 2000 How long (ms) the "Copied!" state persists before reverting
iconOnly bool false Render a bare icon-only button (no border/label) — gray glyph that pops green on copy. Requires aria-label.
scope string|null null Scoped personalization key

Events

The button dispatches wirekit:copied once the write has succeeded or been refused. That is later than the label, which changes at once: event.detail.succeeded is the result, not the guess. The event bubbles, its target is the button, and every copy dispatches it, a second one with the same result as well. The code block's copy button dispatches the same event.

document.addEventListener('wirekit:copied', (event) => {
    countCopy(event.target, event.detail.succeeded);
});

Accessibility

  • The button is a native <button type="button"> — fully keyboard-accessible
  • Icons: aria-hidden="true" on both SVGs (decorative — the text label is the accessible name)
  • Live region: role="status" + aria-live="polite" announces "Copied to clipboard" to screen readers when the copy succeeds
  • Focus ring visible via focus-visible:ring

The slot content (e.g., "Copy Link") serves as the button's accessible name. If you need a different accessible name, pass an aria-label attribute.

Keyboard Interaction

Key Action
Tab Move focus to the button
Enter / Space Activate the button

Pitfalls

  • Don't bind wire:click on the button — use the component's built-in :value prop. The Alpine handler writes via the Clipboard API; a wire:click server roundtrip would race the clipboard write.

Design Tokens

Token Used for
--font-wk-sans Button font family
--font-wk-body-weight Font weight
--text-wk-md Font size
--color-wk-text Default label text
--color-wk-success-text Copied checkmark color
--color-wk-bg-elevated Button background
--color-wk-bg-hover Hover background
--color-wk-border Button border
--color-wk-ring Focus ring
--ring-wk-width Focus ring width
--border-wk-width Border width
--radius-wk-md Border radius
--shadow-wk-sm Subtle shadow
--gap-wk-sm Icon-to-label gap
--padding-wk-x-md / --padding-wk-y-sm Button padding
--transition-wk-duration / --transition-wk-easing Hover / icon-swap transition

Personalization

Override classes globally via WireKit::personalize(). Pass a closure, not a string — a closure receives the shipped block and you add to it; a string replaces it outright, and the shipped block is where focus-visible:ring-[var(--color-wk-ring)] lives:

use Pushery\WireKit\WireKit;

WireKit::personalize('clipboard-button', [
    // The vendor default arrives as $vendor. Append, and the focus ring, the hover
    // surface and the transition tokens all survive the change.
    'base' => fn (string $vendor): string => $vendor
        .' rounded-[var(--radius-wk-full)] px-[var(--padding-wk-x-lg)]',
]);

A string override is legitimate and costs more than it looks. It makes the block yours from that moment on, so every later improvement to it stops arriving — and nothing reports that, because nothing is wrong. See Class overrides via config for both forms.

Further Reading

Updated in WireKit v2.66.0 (2026-10-13)

Was this page helpful?

Thank you for your feedback!

Voting requires cookies or local storage. What we store