Skip to content

Copy Directive

The v-copy directive is used to copy text to the clipboard. It is useful for copying text from a <textarea>, <input> or any other element.

Usage

Lorem ipsum dolor sit amet consectetur adipisicing elit. Libero autem fugiat dolorum, aperiam repellat eos ipsam obcaecati modi, similique harum dignissimos reiciendis cum, nobis delectus soluta ut quae! Nobis, explicabo.

View Code
vue
<template>
  <div class="w-full bg-gray-50 dark:bg-zinc-800 rounded-3xl p-5">
    <textarea
      v-model="text"
      class="w-full p-2 resize-none h-24 rounded-2xl bg-zinc-100 dark:bg-zinc-700"
    ></textarea>
    <button
      v-copy.lowercase="text"
      @copied="handleCopy"
      @copyerror="handleCopyError"
      class="px-4 py-2 bg-indigo-600 text-white rounded-lg"
    >
      {{ btnText }}
    </button>
    <p id="elem">
      Lorem ipsum dolor sit amet consectetur adipisicing elit. Libero autem
      fugiat dolorum, aperiam repellat eos ipsam obcaecati modi, similique harum
      dignissimos reiciendis cum, nobis delectus soluta ut quae! Nobis,
      explicabo.
    </p>
    <button
      v-copy:id="'elem'"
      @copied="handleCopyPara"
      class="px-4 py-2 bg-indigo-600 text-white rounded-lg"
    >
      {{ elemBtn }}
    </button>
  </div>
</template>

<script setup>
import { ref } from "vue";

const text = ref(null);
const btnText = ref("Copy");
const elemBtn = ref("Copy paragraph");
const handleCopy = () => {
  btnText.value = "Copied";
  setTimeout(() => {
    btnText.value = "Copy";
  }, 1500);
  console.log("Text copied successfully");
};
const handleCopyPara = () => {
  elemBtn.value = "Copied";
  setTimeout(() => {
    elemBtn.value = "Copy paragraph";
  }, 1500);
  console.log("Text copied successfully");
};

const handleCopyError = () => {
  btnText.value = "Error";
  setTimeout(() => {
    btnText.value = "Copy";
  }, 1500);
  console.error("Error copying text");
};
</script>

Dynamic Content

vue
<template>
  <div>
    <textarea v-model="text"></textarea>
    <button v-copy="text">Copy</button>
  </div>

  <div>
    <input v-model="text2" />
    <button v-copy="text2">Copy</button>
  </div>
</template>

Arguments

The v-copy directive accepts only one argument id. The id argument is used to copy the inner content of an element by it's id.

ArgumentDescription
idCopy the element's inner content by it's id

id

vue
<template>
  <div>
    <p id="elemId">
      This content will be copied when you click the button, it is being copied
      by id
    </p>
    <button v-copy:id="'elemId'">Copy</button>
  </div>
</template>

Modifiers

The v-copy directive accepts two modifiers uppercase, lowercase, camelcase and pascalcase. The uppercase modifier is used to convert the copied text to uppercase, the lowercase modifier is used to convert the copied text to lowercase, the capitalize modifier is used to convert the copied text to capitalize, and the capitalizeAll modifier is used to convert the copied text to capitalize all.

ModifierDescription
uppercaseConvert the copied text to uppercase
lowercaseConvert the copied text to lowercase
capitalizeConvert the copied text to capitalize
capitalizeAllConvert the copied text to capitalize all

uppercase

vue
<template>
  <div v-copy.uppercase>
    Lorem ipsum dolor sit, amet consectetur adipisicing elit.
  </div>
</template>

Source Code

js
async function copyTextToClipboard(textToCopy, el) {
  try {
    await navigator.clipboard.writeText(textToCopy);
    el.dispatchEvent(new Event("copied"));
  } catch (error) {
    el.dispatchEvent(new Event("copyerror"));
  }
}

const applyModifiers = (text, modifiers) => {
  let modifiedText = text;
  if (modifiers.uppercase) {
    modifiedText = modifiedText.toUpperCase();
  } else if (modifiers.lowercase) {
    modifiedText = modifiedText.toLowerCase();
  } else if (modifiers.capitalize) {
    modifiedText = modifiedText.charAt(0).toUpperCase() + modifiedText.slice(1);
  } else if (modifiers.capitalizeAll) {
    modifiedText = modifiedText
      .split(" ")
      .map((word) => word.charAt(0).toUpperCase() + word.slice(1))
      .join(" ");
  }

  return modifiedText;
};

const clickCopyToClipboard = async (el, textToCopy, modifiers) => {
  const modifiedText = applyModifiers(textToCopy, modifiers);
  await copyTextToClipboard(modifiedText, el);
};

const handleCopy = (el, binding) => {
  let textToCopy;
  if (binding.arg === "id") {
    const elem = document.getElementById(binding.value);
    if (elem) {
      textToCopy = elem.innerText;
    }
  } else {
    textToCopy = binding.value;
  }

  const clickHandler = () =>
    clickCopyToClipboard(el, textToCopy, binding.modifiers);

  el.addEventListener("click", clickHandler);

  // Save the reference to the click handler for removal during the update
  el.clickCopyToClipboard = clickHandler;
};

export const vCopy = {
  mounted(el, binding) {
    handleCopy(el, binding);
  },
  updated(el, binding) {
    el.removeEventListener("click", el.clickCopyToClipboard);
    handleCopy(el, binding);
  },
  unmounted(el) {
    // Remove the click event listener when the directive is unmounted
    el.removeEventListener("click", el.clickCopyToClipboard);
  },
};