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
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>
import * as React from 'react';
import * as ReactDOM from 'react-dom';
import { App } from './App';
ReactDOM.render(<App />, document.getElementById('app'));
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();
}
};
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>
);
};
Top comments (2)
Whats does that react-figma package do ?
Don’t you need to unsubscribe from on message in that useeffect?