Black Weeks — Get lifetime access to all current themes 40% OFF on Full Access.
Use code LEX40 at checkout

Plus, new themes, lifetime updates, use on unlimited projects and lifetime support.

Limited time offer • Use code at checkout
Get full access

How to create a simple copy to clipboard component with Tailwind CSS and Alpine.js

Published on November 11, 2025 by Michael Andreuzza

Building a Copy to Clipboard Utility

Copy-to-clipboard interfaces make it effortless for users to grab snippets like CLI commands, API keys, or short URLs. In this tutorial we will build a polished Alpine.js component that lets visitors edit the text, preview it, and copy it with one click while receiving real-time feedback.

What You’ll Learn

  • Structuring a compact copy module with Tailwind CSS utility classes
  • Managing component state and clipboard operations with Alpine.js
  • Providing instant visual feedback when the copy succeeds
  • Keeping the input, preview, and button perfectly in sync
  • Handling clipboard failures gracefully for better resilience

Component Overview

The component lives inside a rounded card (max-w-xl p-6 mx-auto) and exposes a small form with a text input, a syntax-highlight ready preview using <pre><code>, and an actionable button. Alpine.js powers the reactive text binding and handles the asynchronous clipboard interaction.

Alpine.js State and Behavior

x-data="{ text: 'npm create astro@latest', copied: false, async copy() { try {
await navigator.clipboard.writeText(this.text); this.copied = true;
setTimeout(() => (this.copied = false), 1800); } catch (e) { console.error('Copy
failed', e); } } }"
  • text: Holds the current value from the input and keeps the preview in sync via x-model and x-text bindings.
  • copied: Toggles the feedback UI. When true, the button swaps to a “Copied” label and the status message fades in.
  • copy(): Awaits the clipboard write. On success, it shows feedback for 1.8 seconds; on failure, it logs the error (you could replace this with a toast or alert).

Layout Details

  1. Label & Input — Uses accessible labeling (for="copyInput") and Tailwind focus styles so keyboard users can tab efficiently.
  2. Preview Panel — A pre > code block mirrors the text. Thanks to Alpine’s x-text, it updates instantly when the user types.
  3. Copy Button — The Button component (from the Lexington design system) switches between “Copy” and the success state via x-show.
  4. Status Message — Uses role="status" and aria-live="polite" to ensure screen readers announce the copy confirmation.
  5. Transitionsx-transition.opacity adds a subtle fade for the confirmation message for a refined touch.

Accessibility Touches

  • aria-live="polite" announces the success message without interrupting users.
  • Focus states and clear contrast on the button make the action obvious.
  • The input remains editable after copying, so the user can tweak commands without reloading the component.

Complete Code

<div
  class="max-w-xl p-6 mx-auto rounded-xl bg-sand-50"
  x-data="{
    text: 'npm create astro@latest',
    copied: false,
    async copy() {
      try {
        await navigator.clipboard.writeText(this.text);
        this.copied = true;
        setTimeout(() => (this.copied = false), 1800);
      } catch (e) {
        console.error('Copy failed', e);
      }
    }
  }"
>
  <div class="space-y-4">
    <label for="copyInput" class="block text-sm font-medium text-base-700"
      >Text to copy</label
    >
    <input
      id="copyInput"
      type="text"
      x-model="text"
      class="w-full h-11 px-3 text-sm rounded-md border border-base-100 placeholder-base-400 focus:border-accent-500 focus:ring-accent-500"
      placeholder="Type something to copy..."
    />

    <div class="flex items-center justify-between gap-3">
      <pre
        class="flex-1 p-3 overflow-x-auto text-sm bg-base-50 rounded-md border border-base-100 text-base-600"
      ><code x-text="text"></code></pre>

      <button size="sm" variant="accent" type="button" @click="copy()">
        <span x-show="!copied">Copy</span>
        <span x-show="copied" class="inline-flex items-center gap-1">
          <svg
            xmlns="http://www.w3.org/2000/svg"
            viewBox="0 0 24 24"
            fill="currentColor"
            class="size-4"
          >
            <path
              fill-rule="evenodd"
              d="M2.25 12a9.75 9.75 0 1119.5 0 9.75 9.75 0 01-19.5 0zm14.03-2.28a.75.75 0 10-1.06-1.06L10.5 13.44l-1.97-1.97a.75.75 0 10-1.06 1.06l2.5 2.5a.75.75 0 001.06 0l5.31-5.31z"
              clip-rule="evenodd"
            ></path>
          </svg>
          Copied
        </span>
      </button>
    </div>

    <p
      class="text-xs text-base-500"
      role="status"
      aria-live="polite"
      x-show="copied"
      x-transition.opacity
    >
      The text was copied to your clipboard.
    </p>
  </div>
</div>

You now have a reusable copy-to-clipboard component that pairs Alpine.js interactivity with Tailwind CSS styling. Drop it into your documentation sites, onboarding flows, or any interface where quick copying makes life easier.

/Michael Andreuzza

Did you like this tutorial? Please share it with your friends!