@svelte-put/copy GitHub

action & utilities to copy text to clipboard

@svelte-put/copy @svelte-put/copy @svelte-put/copy changelog

Installation

npm install --save-dev @svelte-put/copy
pnpm add -D @svelte-put/copy
yarn add -D @svelte-put/copy

Minimal Usage

By default, copy will trigger a click 1 event listener on the same node 2 it is used on. The triggered listener will then copy innerText of said node 3 to the default clipboard. The next sections show how 1, 2, 3 can be customized.

Below is a minimal demo of copy with default options. Try clicking on the green button to observe the text within being copied to the blue box.

Example
->
<script lang="ts">
  import { copy, type CopyDetail } from '@svelte-put/copy';
  import { fade } from 'svelte/transition';

  let copied = '';
  function handleCopied(e: CustomEvent<CopyDetail>) {
    copied = e.detail.text;
  }
</script>

<div class="not-prose grid grid-cols-[1fr,auto,1fr] items-center gap-2">
  <button
    class="bg-green-500 p-2 active:scale-95"
    type="button"
    use:copy
    on:copied={handleCopied}
  >
    <strong class="text-blue-500">Click</strong> <span class="text-black">to copy this</span>
  </button>
  <div>-></div>
  <div class="grid place-items-center self-stretch bg-blue-200 text-black">
    {#if copied}
      <p in:fade={{ duration: 200 }}>
        {copied}
      </p>
    {/if}
  </div>
</div>

1 Customizing the Event Types

Pass one or more event types to the event option.

<script>
  import { copy } from '@svelte-put/copy';
</script>

<button type="button" use:copy={{ event: 'mousedown' }}>...</button>
<button type="button" use:copy={{ event: ['pointerenter', 'pointerleave'] }}>...</button>

2 Customizing the Trigger

The trigger option, which typically takes an HTMLElement, specifies the node to which the event listener is attached.

The copy button seen in code blocks on this site is powered by this very action.

A typical use case is clicking on a node to copy text within some other node. In the demo below, try clicking on the green button to copy text in the yellow-bordered box.

Example
to

copy this

->
<script lang="ts">
  import { copy, type CopyDetail } from '@svelte-put/copy';
  import { fade } from 'svelte/transition';

  let trigger: HTMLButtonElement;
  let copied = '';
  function handleCopied(e: CustomEvent<CopyDetail>) {
    copied = e.detail.text;
  }
</script>

<div class="not-prose grid grid-cols-[0.5fr,auto,0.5fr,auto,1fr] items-center gap-4">
  <button class="bg-green-500 p-2 text-black active:scale-95" type="button" bind:this={trigger}>
    Click
  </button>
  <div>to</div>
  <div
    class="grid place-items-center border border-yellow-500 p-2"
    use:copy={{ trigger }}
    on:copied={handleCopied}
  >
    <p>copy this</p>
  </div>
  <div>-></div>
  <div class="grid place-items-center self-stretch bg-blue-200 text-black">
    {#if copied}
      <p in:fade={{ duration: 200 }}>
        {copied}
      </p>
    {/if}
  </div>
</div>

3 Customizing How Text is Copied

The text option can receive a literal string or a sync/async callback that returns a string, which if provided, will be used for copying instead of the default innerText attribute of the node the action is placed on.

Literal

<button use:copy={{ text: 'some static text' }}>Copy me</button>

Callback

Here, text is a callback; its parameter contains information about the forwarded event, reference to node (on which the action is placed), and the trigger (to which event is attached).

Example
->
<script lang="ts">
  import { copy, type TextResolverInput } from '@svelte-put/copy';
  import { fade } from 'svelte/transition';

  let copied = '';
  function copyText(input: TextResolverInput<'pointerdown'>) {
    const { node } = input;
    copied = `Custom - ${node.innerText}`;
    return copied;
  }
</script>

<div class="not-prose grid grid-cols-[1fr,auto,1fr] items-center gap-2">
  <button
    class="bg-green-500 p-2 text-black active:scale-95"
    type="button"
    use:copy={{ event: 'pointerdown', text: copyText }}
  >
    Click
  </button>
  <div>-></div>
  <div class="grid place-items-center self-stretch bg-blue-200 text-black">
    {#if copied}
      <p in:fade={{ duration: 200 }}>
        {copied}
      </p>
    {/if}
  </div>
</div>

Simulating the copy Event

If the synthetic option is set to true, a “fake” copy event will be dispatched alongside the copied CustomEvent, should that be of any use.

Note that since this synthetic copy event is not “real”, operations on clipboardData will have no effect on the actual copied text.

Example
->
<script lang="ts">
  import { copy } from '@svelte-put/copy';
  import { fade } from 'svelte/transition';

  let copied = '';
  function onSyntheticCopy(e: ClipboardEvent) {
    const clipboardData = e.clipboardData;
    copied = clipboardData?.getData('text/plain') ?? '';
    // clipboardData.setData will have no effect here
  }
</script>

<div class="not-prose grid grid-cols-[1fr,auto,1fr] items-center gap-2">
  <button
    class="bg-green-500 p-2 active:scale-95"
    type="button"
    use:copy={{ synthetic: true }}
    on:copy={onSyntheticCopy}
  >
    <strong class="text-blue-500">Click</strong> <span class="text-black">synthetic copy</span>
  </button>
  <div>-></div>
  <div class="grid place-items-center self-stretch bg-blue-200 text-black">
    {#if copied}
      <p in:fade={{ duration: 200 }}>
        {copied}
      </p>
    {/if}
  </div>
</div>

The copyToClipboard Helper

Behind the scene, copy uses a copyToClipboard helper (See its source code here). You may skip the action and use this utility directly to build your own custom solution that fits your exact need.

import { copyToClipboard } from '@svelte-put/copy';

export function customCopy(text: string) {
  copyToClipboard(text);
}

Happy copying! 👨‍💻


a meme showing an episode of Mr. Bean where he is copying another student's answers during an exam
This package was made during Covid. Or was it? How could I remember anything from those years...

Edit this page on Github