Skip to main content
Question

Write to Clipboard from custom plugin


c5inco

Considering that document.execCommand is deprecated, what would the best way to write to the system clipboard within a custom plugin?

Right now, it’s a bit hacky where one can create a temporary HTML UI, set elements like textarea with the intended value to copy, then use document.execCommand. That will likely not be supported in the future, so ideally there’d be a nice and secure way to work with the system clipboard through a Figma API.

19 replies

Carlos_Garcia

Same question here…
I’m looking into developing a widget that would cut some serious overhead for our PO/PM teams, but I need to be able to put content into the User’s clipboard from the Plugin.
@anon21722796 although I’ve searched everywhere – and this would seem like a fairly common use-case – I haven’t been able to figure this one out.

Thanks in advance for your help!


111749
  • 12 replies
  • January 8, 2022

There are two common ways to copy text to clipboard.

  1. Use document.execCommand. However, this is deprecated as mentioned by @c5inco
  2. Use Clipboard API. Note that the operation is asynchronous

Both of the methods rely on browser so they can only run in UI part of figma plugin. I have wrapped the logic inside a utility funciton that can be used out of box. copyToClipboard and copyToClipboardAsync. Hope this helps 😀


Chris_Sinco
  • New Member
  • 3 replies
  • February 2, 2022

Thanks for the figx library! Have you had any issues on copyToClipboard / execCommand approach for plugins running in Figma browser? Right now, I have similar code that seems to work in desktop Figma only.


111749
  • 12 replies
  • February 2, 2022

Thanks for pointing out. Yep I just checked and it doesn’t work in browser. I think the reason is execCommand('copy') not working properly in browser. Figma desktop is a bit different since the plugin UI is wrapped inside a iframe. Anyway, I updated my code (npm@0.0.16) a little bit with a backup window.copy in browser which ONLY works in chrome. Please let me know if you have any suggestions or observations that could make it work universally 😀


c5inco
  • Author
  • 7 replies
  • February 4, 2022

Unfortunately that addition didn’t work for me, even in Chrome. Here is the code I’m using (repurposed from your library - thanks!). Maybe it’s because I’m doing the calls within ui.html. What makes it even more frustrating is the only way I can test this is by publishing the plugin since the browser version doesn’t support plugin development 😑

Have you run into these issues as well?


111749
  • 12 replies
  • February 8, 2022

I don’t have a published plugin to test this. I tested this in the console by switching the frame the plugin is run in.

Have you tried this?


c5inco
  • Author
  • 7 replies
  • July 21, 2022

Sorry on the delay. Yes I did just try this. It works fine in the desktop app, but I’m still running into the same issues in production on Chrome. Have others reported this same issue with your library?


realvjy
  • 1 reply
  • July 26, 2022

I’m trying to use clipboard in a widget, it doesn’t seem possible without creating a plugin UI. Can we do that without showUI? When I hide the visible: false it’s not working.

<AutoLayout
        name="color"
        direction="horizontal"
        horizontalAlignItems="start"
        verticalAlignItems="center"
        spacing={"auto"}

        // Trying solve for copy on click
        onClick={(e) => {
          return new Promise((resolve) => {
            figma.showUI(__html__, {
              visible: true,
              height: 0,
              width: 0,
            });

            figma.ui.postMessage("copy text");
            setTimeout(() => {
              resolve(null);
            }, 100);
          });
        }}
      >

Currently, Plugin UI appear for a second that looks weird


  • 1 reply
  • March 1, 2023

Hey, Is there any update on this?
I’m writing a plugin and testing in the Desktop version and I can only seem to use the method that creates temporary HTML UI which seems quite hacky.
@111749 I tried both methods you posted from Figx but yeah neither worked. It seems that we don’t get access to navigator.clipboard or window.copy.
Has anyone tried to contact Figma regarding this?


Francesco_Bianchi1

I’m also looking for a solution. I’ve tried:

  • listening to ‘copy’ events (user presses ‘cmd + C’) and overriding the copied data. This works but I can’t manually trigger it say, when the user clicks a button (which is what I’d like to have).
  • manually dispatching a ClipboardEvent with the desired data. In this case the event gets dispatched, seemingly correctly, but the data is NOT actually saved in the clipboard.
    I’ll post the code in case somebody can continue from here:
// case 1 - intercept the copy event
// this works but the user needs to press cmd + C
window.addEventListener('copy', (event: any) => {
            event.preventDefault()
            event.clipboardData?.setData(
               'text/plain',
               'This has been programmatically copied!'
            )
}

// case 2 - manually triggering the event
// this can get triggered clicking a button, but the data is not really saved to the clipboard
const dataTransfer = new DataTransfer()
dataTransfer.setData('text/plain', 'hello world')

const event = new ClipboardEvent('copy', {
      clipboardData: dataTransfer,
})
// manually dispatch it
window.dispatchEvent(event)

Finally, I’ve also tried mixing the two techniques but without success.


  • 1 reply
  • March 20, 2023

Hey @harv_y , would you mind sharing the method that creates temporary HTML UI? I tried many different things and I was unable to copy anything to the clipboard, so at this point I’m open to any method that works, even if it’s hacky 😀


jk2K
  • New Participant
  • 14 replies
  • April 30, 2023

Alex_FG
  • New Participant
  • 32 replies
  • May 2, 2023

Here is the code I use which seems to work reliably:

function writeTextToClipboard(str) 
{
    const prevActive = document.activeElement;
    const textArea   = document.createElement('textarea');

    textArea.value = str;

    textArea.style.position = 'fixed';
    textArea.style.left     = '-999999px';
    textArea.style.top      = '-999999px';
    
    document.body.appendChild(textArea);
    
    textArea.focus();
    textArea.select();
    
    return new Promise((res, rej) => 
    {
        document.execCommand('copy') ? res() : rej();
        textArea.remove();
        
        prevActive.focus();
    });
}
function readTextFromClipboard() 
{
    let textArea = document.createElement('textarea');

    textArea.style.position = 'fixed';
    textArea.style.left     = '-999999px';
    textArea.style.top      = '-999999px';
    
    document.body.appendChild(textArea);
    
    textArea.focus();
    textArea.select();
    
    return new Promise((res, rej) => 
    {
        document.execCommand('paste') ? res(textArea.value) : rej();
        textArea.remove();
    });
}

But in cases where I’m working with a HTML input element, I don’t actually do anything myself and just let the system do its thing.


BennoDev
  • 8 replies
  • May 17, 2023

This is what I’m using 🙂
Its inspired by: javascript - Copy text to clipboard: Cannot read properties of undefined reading 'writeText' - Stack Overflow

import { logger } from '../../../shared';

/**
 * Unsecured fallback for copying text to clipboard
 * @param text - The text to be copied to the clipboard
 */
function unsecuredCopyToClipboard(text: string) {
  // Create a textarea element
  const textArea = document.createElement('textarea');
  textArea.value = text;
  document.body.appendChild(textArea);

  // Focus and select the textarea content
  textArea.focus();
  textArea.select();

  // Attempt to copy the text to the clipboard
  try {
    document.execCommand('copy');
  } catch (e) {
    logger.error('Unable to copy content to clipboard!', e);
  }

  // Remove the textarea element from the DOM
  document.body.removeChild(textArea);
}

/**
 * Copies the text passed as param to the system clipboard
 * Check if using HTTPS and navigator.clipboard is available
 * Then uses standard clipboard API, otherwise uses fallback
 *
 * Inspired by: https://stackoverflow.com/questions/71873824/copy-text-to-clipboard-cannot-read-properties-of-undefined-reading-writetext
 * and https://forum.figma.com/t/write-to-clipboard-from-custom-plugin/11860/12
 *
 * @param content - The content to be copied to the clipboard
 */
export function copyToClipboard(content: string) {
  // If the context is secure and clipboard API is available, use it
  if (
    window.isSecureContext &&
    typeof navigator?.clipboard?.writeText === 'function'
  ) {
    navigator.clipboard.writeText(content);
  }
  // Otherwise, use the unsecured fallback
  else {
    unsecuredCopyToClipboard(content);
  }
}

Ruslan
  • 1 reply
  • May 25, 2023

Hi,
Can you give a hit how to use it in code.js for noobs?


BennoDev
  • 8 replies
  • June 4, 2023

My shown approach only works in the ui.js part of a Figma Plugin… I could not figure out how to do it in the code.js part… Thus I send an event back to the ui.js part and copy it there to the clipboard… Since copying text to the user’s clipboard is somewhat a user “interaction” / ui task, I’m also preferring to have the clipboard logic in the ui.js part now 🙂

Example:

    uiHandler.registerEvent({
      type: 'figma.message',
      key: 'intermediate-format-export-result-event',
      callback: async (instance: TUIHandler, args) => {
        setIsLoadingIntermediateFormatExport(false);
        if (args.type === 'success') {
          setContent(args.content);
          copyToClipboard(JSON.stringify(args.content));
        }
      },
    });

Note that’ve written a uiHandler to make the interaction between code.js and ui.js more typesafe and seamless… But I hope you understand the core concept 🙂
cheers


jk2K
  • New Participant
  • 14 replies
  • August 26, 2023

me to, desktop Figma only, browser is not work


FWExtensions

The fwidgets UI library for Figma plugins makes it really easy to copy something to the clipboard. Here’s an example of copying the width and height of the selected element to the clipboard as a JS object:

// main.ts
import fwidgets from "fwidgets/main";

export default () => fwidgets(async ({ output }) => {
	const el = figma.currentPage.selection[0];
	
	if (el) {
		const { width, height } = el;
		await output.clipboard({ width, height });
	}
});

That will show the smallest possible plugin window at the bottom-right of the screen, which is necessary to perform the copy. But you don’t have to worry about setting up that UI code or dealing with messaging between the main and UI threads.

fwidgets isn’t just for copying to the clipboard, though. It also makes it easy to collect input from the user by showing UI controls with simple one-liners, for when you just want to focus on the scripting and not worry about building a whole plugin interface.


Chris_Sinco
  • New Member
  • 3 replies
  • April 1, 2025

...and now this API is broken in my plugin. What is the recommended way for plugins to copy contents to the clipboard now?


Reply


Cookie policy

We use cookies to enhance and personalize your experience. If you accept you agree to our full cookie policy. Learn more about our cookies.

 
Cookie settings