Home / Blog / How to Copy to Clipboard In Vue
How to Copy to Clipboard In Vue

How to Copy to Clipboard In Vue

Daniel Kelly
Daniel Kelly
Updated: September 16th 2025

Copy-to-clipboard is a tiny UX win that makes apps feel polished—share links, copy API keys, invite codes, or code snippets. The easiest way to ship it in Vue is with VueUse’s useClipboard composable. After that, we’ll cover zero-dependency (native) patterns, a directive, SSR gotchas, and accessibility tips.

TL;DR - The Simplest Way to Implement Copy to Clipboard in Vue with VueUse

npm i @vueuse/core
<script setup lang="ts">
import { ref } from 'vue'
import { useClipboard } from '@vueuse/core'

const source = ref('Hello from Vue + VueUse!')
const { copy, copied, isSupported } = useClipboard({ source })

async function onCopy() {
  await copy() // copies source.value
  // You can also do copy('override per call')
}
</script>

<template>
  <div>
    <code>{{ source }}</code>
    <button @click="onCopy" :disabled="copied">
      <span v-if="copied">Copied ✓</span>
      <span v-else>Copy</span>
    </button>

    <p aria-live="polite" v-if="!isSupported">
      Clipboard unsupported; using fallback.
    </p>
  </div>
</template>

Handy Variations of useClipboard

  • Copy dynamic strings without wiring a ref:
const { copy } = useClipboard()
copy('https://example.com/invite/abc123')
  • Copy from a DOM element’s text:
<script setup lang="ts">
import { ref, nextTick } from 'vue'
import { useClipboard } from '@vueuse/core'

const el = ref<HTMLElement | null>(null)
const { copy, copied } = useClipboard()

async function copyFromElement() {
  await nextTick();
  const text = el.value?.textContent ?? ''
  if (text) copy(text)
}
</script>

<template>
  <pre ref="el">npm create vue@latest</pre>
  <button @click="copyFromElement">{{ copied ? 'Copied!' : 'Copy' }}</button>
</template>
  • Read from the clipboard (opt-in):
import { useClipboard } from '@vueuse/core'
const { text, read } = useClipboard({ read: true })
await read()
console.log(text.value)

Copy To Clipboard In a Nuxt App

Copying to clipboard in a Nuxt app is also possible with VueUse’s useClipboard. Just make sure to call copy in event handlers or inside onMounted as the underlying navigator dependency doesn’t exist on the server.

A Tiny v-copy Directive (Powered by VueUse)

When you want a one-liner on buttons or icons everywhere it’s easy enough repurpose VueUse’s composable as a custom directive.

// /directives/copy.ts
import type { Directive } from 'vue'
import { useClipboard } from '@vueuse/core'

export const vCopy: Directive<HTMLElement, string | undefined> = {
  mounted(el, binding) {
    const { copy } = useClipboard()
    el.addEventListener('click', () => {
      const value =
        binding.value ??
        el.getAttribute('data-copy') ??
        el.textContent ??
        ''
      copy(value)
      el.dispatchEvent(new CustomEvent('copied'))
    })
  },
}

Make sure to register the directive.

// main.ts
import { createApp } from 'vue'
import App from './App.vue'
import { vCopy } from './directives/copy'

createApp(App).directive('copy', vCopy).mount('#app')

Then use it whenever you need it!

<button v-copy="'https://example.com/invite/abc'">Copy Invite Link</button>
<button v-copy data-copy="hard-coded">Copy Hard-Coded</button>
<button v-copy @copied="toast('Copied!')">Copy & Toast</button>

Zero-Dependency Option (Native Clipboard API)

If you prefer no dependencies, the native API is concise. You can copy and paste the code below into your own projects, it includes a textarea fallback for older/quirky browsers.

// /composables/useClipboard.native.ts
import { ref } from 'vue'
export function useClipboard() {
  const copied = ref(false)
  const error = ref<unknown>(null)
  const isSupported = typeof navigator !== 'undefined' && !!navigator.clipboard

  function fallbackWriteText(text: string) {
    const ta = document.createElement('textarea')
    ta.value = text
    ta.setAttribute('readonly', '')
    ta.style.position = 'fixed'
    ta.style.left = '-9999px'
    document.body.appendChild(ta)
    ta.select()
    const ok = document.execCommand('copy')
    document.body.removeChild(ta)
    return ok
  }

  async function copy(text: string) {
    error.value = null
    try {
      if (isSupported) await navigator.clipboard.writeText(text)
      else if (!fallbackWriteText(text)) throw new Error('execCommand failed')
      copied.value = true
      setTimeout(() => (copied.value = false), 1200)
      return true
    } catch (e) {
      error.value = e
      return false
    }
  }
  return { copy, copied, error, isSupported }
}

Accessibility (a11y) Tips for Copy-to-Clipboard in Vue.js

  • Use clear labels (not just an icon).
  • Announce status with aria-live="polite".
  • Don’t steal focus; keep feedback subtle (toast/badge).
  • Respect reduced motion in any “Copied!” animation.
<span class="sr-only" aria-live="polite">{{ copied ? 'Copied' : '' }}</span>

Common Pitfalls with useClipboard

  • Not HTTPS / not localhost: Clipboard API is secure-context only.
  • No user gesture: Trigger from a click/tap.
  • Safari quirks: Keep a fallback for older versions (provided by VueUse out of the box).

Final Checklist for Copy to Clipboard In Vue.js

  • [ ] Start with VueUse useClipboard for the fastest and easiest path
  • [ ] Repurpose useClipboard as a directive if you prefer
  • [ ] For a zero dependency solution, copy the example from this article

Finally, useClipboard is only one of VueUse’s many helpful composables. If you want to discover even more powerful helpers from this amazingly popular library, checkout our full VueUse course. In it, we go over useClipboard but also many other helpful composables like useDark, useFavicon, useInterval, and more!

Start learning Vue.js for free

Daniel Kelly
Daniel Kelly
Daniel is the lead instructor at Vue School and enjoys helping other developers reach their full potential. He has 10+ years of developer experience using technologies including Vue.js, Nuxt.js, and Laravel.

Comments

Latest Vue School Articles

State Based Favicons (Tab Alerts) in Vue

State Based Favicons (Tab Alerts) in Vue

Add state-based favicons in Vue to show unread counts, alerts, or status dots with VueUse for subtle, polished tab notifications.
Daniel Kelly
Daniel Kelly
Master AI-Driven Development: Free Online AIDD Day 2025

Master AI-Driven Development: Free Online AIDD Day 2025

Sept 10, 2025- a free online event - expert-led sessions on AI‑driven development workflows, prompting techniques, CLI agents, and more.
Daniel Kelly
Daniel Kelly
VueSchool logo

Our goal is to be the number one source of Vue.js knowledge for all skill levels. We offer the knowledge of our industry leaders through awesome video courses for a ridiculously low price.

More than 200.000 users have already joined us. You are welcome too!

Follow us on Social

© All rights reserved. Made with ❤️ by BitterBrains, Inc.