DEV Community

seya
seya

Posted on

How to develop Figma plugin with React + TS

TL;DR

git clone https://github.com/kazuyaseki/react-figma-plugin-boilerplate.git <your project name>
yarn or npm install
yarn webpack:watch  or  npm run webpack:watch 
Enter fullscreen mode Exit fullscreen mode

Developing Figma plugin is exciting, but when it comes to develop one with some stateful UI, it's a pain to develop in imperative way.

Thus I created a boilerplate to develop with React and here I introduce it.

The content of boilerplate is as follows.
https://github.com/kazuyaseki/react-figma-plugin-boilerplate

How to render Figma plugin UI with React

There is nothing special, you just have to do ReactDOM.render to ui.html which is specified from manifest.json.

<div id="app"></div>
Enter fullscreen mode Exit fullscreen mode
import * as React from 'react';
import * as ReactDOM from 'react-dom';
import { App } from './App';

ReactDOM.render(<App />, document.getElementById('app'));
Enter fullscreen mode Exit fullscreen mode

Sample Code

I paste a sample code of plugin made with React below.

My plugin is to enable incremental search for components and create an instance from it.(actually Figma itself has this feature but I thought it would be nice when keyboard shortcut functionality is added to plugin)

It works as the following gif(sorry my tweet is in Japanese)

Before you start building your plugin, I recommend you to read How Plugins Run document.

You have to beware that main thread which can reference Figma object is different from plugin UI thread.

So you need to use message object to pass data between the threads, my sample code has codes for both directions, so please refer to it.

import { subscribeOnMessages } from 'react-figma';

figma.showUI(__html__);

const componentNodes = figma.root.findAll((node) => node.type === 'COMPONENT');
const conmponentsData = componentNodes.map((node) => ({
  id: node.id,
  name: node.name,
}));
figma.ui.postMessage(conmponentsData);

figma.ui.onmessage = (message) => {
  subscribeOnMessages(message);

  if (message.type === 'create-instance') {
    const component = figma.root.findOne(
      (node) => node.id === message.id
    ) as ComponentNode;
    component.createInstance();
  }
};
Enter fullscreen mode Exit fullscreen mode
import * as React from 'react';

type ComponentItemType = {
  id: string;
  name: string;
};

export const App = () => {
  const [query, setQuery] = React.useState('');
  const [components, setComponents] = React.useState<ComponentItemType[]>([]);

  React.useEffect(() => {
    onmessage = (event) => {
      setComponents(event.data.pluginMessage as ComponentItemType[]);
    };
  }, []);

  const create = (id: string) => {
    parent.postMessage({ pluginMessage: { type: 'create-instance', id } }, '*');
  };

  return (
    <div>
      <input value={query} onChange={(e) => setQuery(e.target.value)} />
      <div style={{ display: 'flex', flexDirection: 'column' }}>
        {components
          .filter((component) => {
            if (query.length === 0) {
              return true;
            }
            return component.name.includes(query);
          })
          .map((component) => (
            <button onClick={() => create(component.id)}>
              {component.name}
            </button>
          ))}
      </div>
    </div>
  );
};

Enter fullscreen mode Exit fullscreen mode

Top comments (2)

Collapse
 
anilsonix profile image
Anil Kumar Soni

Whats does that react-figma package do ?

Collapse
 
patrickkeenan profile image
Patrick Keenan

Don’t you need to unsubscribe from on message in that useeffect?