Skip to content

Instantly share code, notes, and snippets.

@mattdesl
Created Jun 24, 2019
Embed
What would you like to do?

Figma Plugin Utility: html<->script I/O

Inspired by Electron, a simple event emitter you can use to communicate between your main script and your html UI.

In your script, i.e. main.js:

import { script as io } from './io.js';

// Show the iframe
figma.showUI(__html__);

io.send('start', { hello: 'world' });
io.once('beep', data => {
  console.log('Received', data); // 'boop'
});

And in your html (after bundling with Webpack/Parcel)

import { html as io } from './io.js';

(async () => {
  // Await the first 'start' message
  const msg = await io.async('start');
  console.log(msg.hello); // 'world'

  // Listen for other events
  io.on('image-bytes', bytes => decodeBytes(bytes));

  // Send data back
  io.send('beep', 'boop');
})();

The interface is a Node EventEmitter, so you can use once(), removeListener, etc.

import { EventEmitter } from 'events';
function createInterface (renderer) {
const emitter = new EventEmitter();
const receive = result => {
if (result && result.event) {
emitter.emit(result.event, result.data);
}
};
if (renderer) {
window.onmessage = ev => receive(ev.data.pluginMessage);
} else {
figma.ui.onmessage = data => receive(data);
}
emitter.send = function (event, data) {
if (typeof event !== 'string') {
throw new Error('Expected first argument to be an event name string');
}
const postData = {
event,
data
};
if (renderer) {
window.parent.postMessage({ pluginMessage: postData }, '*');
} else {
figma.ui.postMessage(postData);
}
};
emitter.async = function (ev) {
return new Promise((resolve) => {
this.once(ev, resolve);
});
};
return emitter;
}
const isRenderer = typeof figma === 'undefined';
export const html = isRenderer ? createInterface(true) : undefined;
export const script = isRenderer ? undefined : createInterface();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment