Back to articles

Easily Copy Text to the Clipboard with Alpine.js

December 31st, 2023

If you need to copy text to the clipboard with JavaScript, Alpine.js makes this super easy with the help of a simple plugin.

Just a pre-warning — you'll need to be running HTTPS locally for the JavaScript Clipboard API to work.

To get started, let's imagine we have a URL for a referral program that we'll allow a user to copy to the clipboard with the click of a button. We're using an input here, but in reality, the source of the text you're copying doesn't matter.

<input type="text" id="referral_code" value="{{ $referralCode->link() }}" readonly />
<button>Copy link</button>

The {{ $referralCode->link() }} code simply outputs a unique link to the user's referral code.

The Alpine Clipboard Plugin

The JavaScript API for copying to the clipboard is pretty simple, but the Alpine Clipboard plugin allows us to integrate this functionality into our Alpine.js apps with more flexibility.

Let's install it

npm install @ryangjchandler/alpine-clipboard

Next up, register the plugin wherever you're importing and starting Alpine.js.

import Alpine from 'alpinejs'
import Clipboard from '@ryangjchandler/alpine-clipboard' // Import it

Alpine.plugin(Clipboard) // Register the plugin

window.Alpine = Alpine
window.Alpine.start()

The Alpine Clipboard plugin is now installed.

Basic clipboard copying

Let's add a wrapper to our input and button to create an Alpine component, and then add a click event to copy the value to the clipboard.

<div
  x-data="{
	link: '{{ $referralCode->link() }}',
	copy () {
	  $clipboard(this.link)
	}
  }"
>
  <input type="text" id="referral_code" value="{{ $referralCode->link() }}" readonly />
  <button x-on:click="copy">Copy link</button>
</div>

Clicking the button should now copy the value defined within the Alpine component to the clipboard. Notice I'm using the value twice here, once in the input value and again within the Alpine component. I prefer to do this rather than target the element and grab its value. There are tons of ways to use this plugin, so I'd recommend checking out the docs once we're done.

Sprinkling in some more functionality

Right now, the user has no idea anything has been successfully copied to the clipboard. Let's change that by switching up the button value when the user clicks to copy.

<div
  x-data="{
    link: '{{ auth()->user()->referralLink() }}',
    copied: false,
    copy () {
      $clipboard(this.link)
    
      this.copied = true
    }
  }"
>
  <input type="text" id="referral_code" value="{{ $referralCode->link() }}" readonly />
  <button x-on:click="copy" x-text="copied ? `Copied!` : `Copy link`">Copy link</button>
</div>

Now, using the Alpine x-text directive, this will change the button label to Copied! when the user presses the button.

If you'd like to revert the button label after a period of time, you can do this with a simple timeout in JavaScript.

<div
  x-data="{
    link: '{{ auth()->user()->referralLink() }}',
    copied: false,
    timeout: null,
    copy () {
      $clipboard(this.link)

      this.copied = true

      clearTimeout(this.timeout)

      this.timeout = setTimeout(() => {
        this.copied = false
      }, 3000)
    }
  }"
>
  <input type="text" id="referral_code" value="{{ $referralCode->link() }}" readonly />
  <button x-on:click="copy" x-text="copied ? `Copied!` : `Copy link`">Copy link</button>
</div>

After 3 seconds, the button label will change back to the original text. We also use clearTimeout to reset the timeout so we don't keep too many timeouts running if the user clicks more than once. This avoids any flashing of text.

That's it!

The solution here is the one I use the most to copy to the clipboard with Alpine.js, but the plugin we're using provides other methods. If you need something specific or need to tweak anything, check out the documentation.

Happy copying!

Author
Alex Garrett-Smith