lexington®
Use code LEX40 at checkout
7k+ customers.
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 viax-modelandx-textbindings.copied: Toggles the feedback UI. Whentrue, 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
- Label & Input — Uses accessible labeling (
for="copyInput") and Tailwind focus styles so keyboard users can tab efficiently. - Preview Panel — A
pre > codeblock mirrors the text. Thanks to Alpine’sx-text, it updates instantly when the user types. - Copy Button — The
Buttoncomponent (from the Lexington design system) switches between “Copy” and the success state viax-show. - Status Message — Uses
role="status"andaria-live="polite"to ensure screen readers announce the copy confirmation. - Transitions —
x-transition.opacityadds 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