How to Build a Powerful Figma Plugin from Scratch: A Step‑by‑Step Guide
This article walks through the fundamentals of Figma plugin development, explaining the architecture of main and UI threads, demonstrating core features of the lego‑quiz‑figma plugin, and providing a complete workflow from creating the manifest to publishing the plugin, complete with code examples and best‑practice tips.
1. What Is Figma and What Are Figma Plugins
Purpose: introduce Figma plugins; develop a Figma plugin from 0 to 1; share personal development ideas.
Figma is a browser‑based collaborative UI design tool.
The plugin adds functions such as rotating selected elements together while allowing each element to rotate individually, batch color changes with hierarchical structuring, exporting selected elements as data structures for other programs, importing data, and automating repetitive tasks.
2. Demonstrating Basic Features of lego‑quiz‑figma
Save selected elements as images.
Transform element positions for different scenarios.
Upload images to the backend.
Adjust element order.
Save the current scene for later reuse.
Copy the current position to the clipboard.
Refresh selected elements and re‑execute steps 1‑5.
Format
{
"data": [
{
"type": "container",
"name": "container_01",
"width": 222.78260803222656,
"height": 172.21739196777344,
"x": 16,
"y": 204,
"children": [
{
"type": "image",
"name": "玩具车3",
"width": 75.78260803222656,
"height": 50.21739196777344,
"x": 0,
"y": 0,
"index": [0,9,0],
"url": "https://sf6-ttcdn-tos.pstatp.com/img/edux-data/1627009282549ca8bcf0b17~0x0.png"
},
{
"type": "container",
"name": "container_02",
"width": 170.78260803222656,
"height": 124.21739196777344,
"x": 52,
"y": 48,
"children": [
{
"type": "image",
"name": "Frame_662",
"width": 151.78260803222656,
"height": 124.21739196777344,
"x": 11,
"y": 0,
"index": [0,9,1,0],
"url": "https://sf3-ttcdn-tos.pstatp.com/img/edux-data/1627009282384e5da6a2689~0x0.png"
},
{
"type": "image",
"name": "玩具车2",
"width": 75.78260803222656,
"height": 50.21739196777344,
"x": 95,
"y": 9,
"index": [0,9,1,1],
"url": "https://sf6-ttcdn-tos.pstatp.com/img/edux-data/1627009282222dfb3a4ffa4~0x0.png"
},
{
"type": "image",
"name": "玩具车1",
"width": 75.78260803222656,
"height": 50.21739196777344,
"x": 0,
"y": 45,
"index": [0,9,1,2],
"url": "https://sf3-ttcdn-tos.pstatp.com/img/edux-data/16270092820316c732f80fe~0x0.png"
}
],
"index": [0,9,1]
}
],
"index": [0,9]
}
],
"scene": "ER_L1"
}3. In‑Depth Understanding of Figma Plugins
Component Structure
The plugin consists of two parts: the sandbox (main thread) on the left and an iframe (UI thread) on the right.
The main thread runs in a sandboxed node environment, allowing execution of JavaScript with ES6 APIs to manipulate Figma’s document.
The main thread creates the iframe, injects the HTML we wrote, controls its size, and the UI thread can use normal browser APIs such as network requests and interact with the user.
The UI thread and main thread communicate via postMessage. Only JSON‑compatible data, blob, and arraybuffer can be transferred.
What Figma Plugins Can Do
Read local file layers and their properties.
Plugins can expose element size, position, hierarchy, color, text, etc., and can also modify them.
Design the plugin UI (using an iframe).
Access some browser APIs (excluding indexedDB).
Additional capabilities include network requests, opening files, using canvas, WebGL (e.g., Pixi), WebAssembly, and audio APIs.
4. From Zero to Release: Full Workflow
Create a New Plugin
Open the plugin creation window.
Create manifest.json.
{
"name": "lego-quiz-figma",
"id": "996264569045667578",
"api": "1.0.0",
"main": "dist/code.js",
"ui": "dist/ui.html"
}The id is required for privileged operations; it is generated when you click “Generate new manifest.json”. The main field points to the main thread file, and ui points to the UI file.
Create Main and UI Files
Figma loads the main file first.
Review
The plugin needs two files: the main thread for interacting with Figma and the UI thread for user interaction. In development, we bundle our source code into these files.
Use the official webpack configuration (see the sample repository) and HtmlWebpackInlineSourcePlugin to inline the UI code into ui.html, because Figma only loads files declared in manifest.json.
When using TypeScript, install the typings with npm install --save-dev @figma/plugin-typings to get type definitions for Figma objects.
Write Main Thread Code
/// <reference path="../../node_modules/@figma/plugin-typings/index.d.ts" />
import { receiveUIMessage, sendCurrentMode } from './ui-relation';
figma.showUI(__html__, { visible: true, width: 300, height: 180 });
function start() {
// 1. Set up UI message handler
receiveUIMessage();
// 2. Get current mode and send to UI
sendCurrentMode();
}
start(); Referenceprovides type hints for Figma elements.
The fifth line creates the UI iframe with specified dimensions.
Write UI Code
return (
<div className="upload-image">
<div className="button-group">
<Button loading={loading(imageInfo)} icon={<CopyOutlined />} id="copy-btn" data-clipboard-text={addImageInfo(imageInfo)}>
复制
</Button>
<Button onClick={updateEvent} icon={<RetweetOutlined />}>刷新</Button>
</div>
<p className="label-model">业务场景</p>
<Radio.Group onChange={onChange} value={model}>
<Radio value={Models.COMMON_DEV}>通用</Radio>
<Radio value={Models.ER_L1}>ER L1</Radio>
<Radio value={Models.ER_GAME}>ER 课后练习</Radio>
</Radio.Group>
</div>
);This React snippet builds the UI, which is later bundled into ui.html.
Import Configuration and Execute
After importing manifest.json, the execution flow is: run the main file → create the iframe → inject UI content.
Publish Plugin
Open the plugin management panel in Figma and publish.
5. Details of lego‑quiz‑figma
The main thread file is named code .
Code Details
Message passing via postMessage.
// Send message to UI
export function transferUIData(transferData: CodeToUIData) {
figma.ui.postMessage(transferData);
}
// Receive message from UI
export function receiveUIMessage() {
figma.ui.onmessage = ({ message, type }) => {
if (type === UIToCodeType.UPDATE) {
figmaStorage.setData('model', message);
startDataTransform();
}
};
}Storage API (clientStorage) for persisting data.
export const figmaStorage = {
cache: {},
async getData(key) {
if (this.cache[key]) return this.cache[key];
const value = await figma.clientStorage.getAsync(key);
this.cache[key] = value;
return value;
},
setData(key, value) {
if (this.cache[key] && this.cache[key] === value) return;
this.cache[key] = value;
return figma.clientStorage.setAsync(key, value);
}
};Export selected nodes to a structured format and send to UI.
const nodeData = {
type: exportNodeType,
name: normalName,
width,
height,
x: x + pos.x,
y: y + pos.y,
bytes,
children: null
};Node types include zone (hot area), locateDot (anchor), container (hierarchical group), and image (visual element).
Coordinate conversion accounts for different reference points (global origin vs. nearest container) and handles frame vs. group differences.
const pos = { x: 0, y: 0 };
let temp = findFrameNode(node.parent);
let ratio = 1;
while (temp) {
ratio = temp.width / CONVENTION_SIZE.width;
if (frameMayBeContainer(temp, ratio)) break;
if (temp.parent.type !== 'PAGE') {
pos.x += temp.x;
pos.y += temp.y;
}
temp = findFrameNode(temp.parent);
}Images are exported as Uint8Array and stored in the bytes field.
if (exportNodeType === 'image') {
nodeData.bytes = await node.exportAsync({
format: 'PNG',
constraint: { type: 'SCALE', value: 1.5 }
});
delete nodeData.children;
}UI Details
UI receives messages from the main thread.
window.addEventListener('message', (event) => {
const { type, data } = event.data.pluginMessage;
switch (type) {
case CodeToUIType.UploadImage:
uploadHandler(data);
break;
case CodeToUIType.ModelData:
setModel(data);
break;
case CodeToUIType.COMMON_MESSAGE:
message[data[0]]({
content: data[1],
className: 'message-style',
duration: 2
});
break;
}
});UI sends messages to the main thread.
const sendMessageToCore = (message, type) => {
parent.postMessage({ pluginMessage: { message, type } }, '*');
};Business scenarios differ in coordinate origin and axis direction (COMMON_DEV, ER_L1, ER_GAME).
6. Reflections and Discussion
My Takeaways
Figma plugins can automate repetitive design tasks.
Plugins enable designers to import resources directly, adding surprise value.
The current export format is custom; future work could explore a universal format.
Figma allows exporting images as Uint8Array for fine‑grained control.
Sharing plugin development experiences benefits the community.
Figma’s Outlook for Plugins
More access to files, users, teams, and comments.
Full access to team libraries.
Event‑driven plugin execution (with performance considerations).
Long‑running plugins.
Plugin settings in property panels/toolbars.
Keyboard shortcuts for plugins.
Plugins in the file browser.
Access to version history.
Helper function APIs.
Figma UI components that can import Figma elements.
Plugin error‑report analysis.
References
[1] Figma plugin samples: https://github.com/figma/plugin-samples/tree/master/webpack
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
