Agent skill
figma-plugins
Build Figma plugins with full Plugin API access. Use when creating plugins for Figma, FigJam, Slides, or Dev Mode that manipulate designs, generate content, export assets, manage styles/variables, work with components, integrate external services, or monetize with payments. Covers manifest configuration, sandbox architecture, UI development with React, node manipulation, auto layout, text/font handling, image processing, variables (design tokens), annotations, codegen, payments, data storage, and publishing. Also trigger for Figma Slides plugin development, Dev Mode codegen plugins, or any work involving the Figma Plugin API.
Install this agent skill to your Project
npx add-skill https://github.com/majiayu000/claude-skill-registry/tree/main/skills/other/other/figma-plugins
SKILL.md
Figma Plugin Development
Architecture Overview
Figma plugins consist of three parts:
- manifest.json — Configuration, permissions, and capabilities
- Main code (code.ts) — Runs in QuickJS sandbox with
figmaAPI access - UI (optional) — HTML/CSS/JS iframe with browser APIs
┌─────────────────────────────────────────────────────────┐
│ MAIN THREAD (Sandbox) │ UI IFRAME (Browser) │
│ ✓ figma global API │ ✓ DOM, fetch, Canvas │
│ ✓ Document manipulation │ ✓ React/Vue/Svelte │
│ ✓ Native fetch API │ ✓ WebSockets │
│ ✗ No DOM access │ ✗ No figma API │
│ └────── postMessage ──────┘ │
└─────────────────────────────────────────────────────────┘
Important: The sandbox now has a native Fetch API — network requests no longer require the UI iframe.
Quick Start
Minimal manifest.json
{
"name": "My Plugin",
"id": "000000000000000000",
"api": "1.0.0",
"main": "code.js",
"editorType": ["figma"],
"documentAccess": "dynamic-page"
}
Minimal Plugin (No UI)
const rect = figma.createRectangle();
rect.fills = [figma.util.solidPaint('#FF5500')];
figma.currentPage.appendChild(rect);
figma.closePlugin('Created rectangle');
Plugin with UI
// code.ts
figma.showUI(__html__, { width: 300, height: 200, themeColors: true });
figma.ui.onmessage = async (msg) => {
if (msg.type === 'create-rect') {
const rect = figma.createRectangle();
rect.resize(msg.width, msg.height);
figma.currentPage.appendChild(rect);
}
figma.closePlugin();
};
<!-- ui.html -->
<button id="create">Create</button>
<script>
document.getElementById('create').onclick = () => {
parent.postMessage({ pluginMessage: { type: 'create-rect', width: 100, height: 100 } }, '*');
};
</script>
Manifest Configuration
Required Fields
| Field | Description |
|---|---|
name |
Plugin name in Figma menu |
id |
Unique ID (assigned by Figma on publish) |
api |
API version, always "1.0.0" |
main |
Path to compiled JavaScript entry |
editorType |
Array: "figma", "figjam", "dev", "slides", "buzz" |
documentAccess |
Always "dynamic-page" — mandatory for all new plugins |
Editor Types
Five editor types with distinct API surfaces:
"figma"— Full design editor (default)"figjam"— Whiteboard (stickies, connectors, tables, stamps, timer)"dev"— Dev Mode (read-only, codegen, inspect, dev resources)"slides"— Figma Slides (SlideNode, SlideRowNode, transitions)"buzz"— Figma Buzz (marketing assets, smart resize)
Invalid combinations: ["figjam", "dev"] and ["slides", "dev"].
Permissions
{ "permissions": ["currentuser", "activeusers", "fileusers", "teamlibrary", "payments"] }
Capabilities
{ "capabilities": ["codegen", "inspect", "textreview", "vscode"] }
codegen— Code generation in Dev Mode (requirescodegenLanguages)inspect— Custom inspect panel in Dev Modetextreview— Text review/linting (firestextreviewevent)vscode— VS Code integration
Network Access
{
"networkAccess": {
"allowedDomains": ["api.example.com", "*.example.com"],
"reasoning": "Required for fetching data from our API",
"devAllowedDomains": ["http://localhost:3000"]
}
}
Supports wildcards, WebSocket schemes (ws://, wss://), ["none"], or ["*"] (unrestricted, requires reasoning). Plugin iframes have a null origin — CORS with Access-Control-Allow-Origin: * is required on target servers.
Menu Commands and Mode
{
"menu": [
{ "name": "Create Shape", "command": "create" },
{ "separator": true },
{ "name": "Settings", "command": "settings" }
]
}
figma.on('run', ({ command }) => {
if (command === 'settings') figma.showUI(__uiFiles__['settings']);
});
figma.mode // 'default' | 'textreview' | 'inspect' | 'codegen' | 'linkpreview' | 'auth'
Project Setup
TypeScript Configuration
npm init -y
npm install --save-dev typescript @figma/plugin-typings esbuild
tsconfig.json:
{
"compilerOptions": {
"target": "ES2020", "lib": ["ES2020"], "strict": true,
"typeRoots": ["./node_modules/@types", "./node_modules/@figma"],
"outDir": "./dist"
},
"include": ["src/**/*.ts"]
}
Current @figma/plugin-typings: 1.121.0. Use eslint-plugin-figma-plugins to catch deprecated sync API calls.
Build with esbuild
import * as esbuild from 'esbuild';
await esbuild.build({
entryPoints: ['src/code.ts'], bundle: true, outfile: 'dist/code.js',
target: 'es2020', minify: process.argv.includes('--minify'),
});
Essential Patterns
Selection Handling
const selection = figma.currentPage.selection;
if (selection.length === 0) {
figma.notify('Select at least one layer', { error: true });
figma.closePlugin();
return;
}
for (const node of selection) {
if (node.type === 'TEXT') { /* process */ }
}
Node Creation
const rect = figma.createRectangle();
const frame = figma.createFrame();
const text = figma.createText();
const component = figma.createComponent();
const section = figma.createSection();
const slide = figma.createSlide(); // Slides
const slideRow = figma.createSlideRow(); // Slides
node.x = 100; node.y = 200;
node.resize(300, 200);
frame.appendChild(rect);
Colors and Fills
RGB values are 0-1, not 0-255.
const solidPaint = figma.util.solidPaint('#FF5500');
rect.fills = [{ type: 'SOLID', color: { r: 1, g: 0.5, b: 0 }, opacity: 0.8 }];
// CRITICAL: Clone before modifying — fills/strokes/effects are read-only
const fills = JSON.parse(JSON.stringify(rect.fills));
fills[0].color.r = 0.5;
rect.fills = fills;
Text (MUST Load Fonts)
const text = figma.createText();
await figma.loadFontAsync({ family: 'Inter', style: 'Regular' });
text.characters = 'Hello World';
text.fontSize = 24;
text.fontName = { family: 'Inter', style: 'Bold' };
text.setRangeFontSize(0, 5, 32);
text.setRangeFills(0, 5, [figma.util.solidPaint('#FF0000')]);
text.setRangeHyperlink(0, 5, { type: 'URL', value: 'https://example.com' });
if (text.fontName === figma.mixed) {
const fonts = text.getRangeAllFontNames(0, text.characters.length);
await Promise.all(fonts.map(f => figma.loadFontAsync(f)));
}
Auto Layout
const frame = figma.createFrame();
frame.layoutMode = 'HORIZONTAL'; // 'VERTICAL' | 'NONE'
frame.primaryAxisSizingMode = 'AUTO';
frame.counterAxisSizingMode = 'AUTO';
frame.itemSpacing = 16;
frame.paddingTop = frame.paddingBottom = 20;
frame.paddingLeft = frame.paddingRight = 16;
frame.primaryAxisAlignItems = 'CENTER';
frame.counterAxisAlignItems = 'CENTER';
frame.layoutWrap = 'WRAP';
frame.counterAxisSpacing = 12;
child.layoutSizingHorizontal = 'FILL'; // 'FIXED' | 'HUG' | 'FILL'
Variables (Design Tokens)
Variables are the API for design tokens. See references/variables-api.md for the full reference.
const collection = figma.variables.createVariableCollection('Colors');
const primaryColor = figma.variables.createVariable('primary', collection, 'COLOR');
primaryColor.setValueForMode(collection.defaultModeId, { r: 0.2, g: 0.4, b: 1 });
rect.setBoundVariable('fills', primaryColor); // 30+ bindable fields
rect.setBoundVariable('cornerRadius', radiusVar);
rect.setBoundVariable('itemSpacing', spacingVar);
const allColors = await figma.variables.getLocalVariablesAsync('COLOR');
const libVar = await figma.variables.importVariableByKeyAsync(key);
Annotations
node.annotations = [{
label: 'Ready for development',
properties: [{ type: 'fills' }, { type: 'width' }, { type: 'fontSize' }]
}];
const categories = await figma.annotations.getAnnotationCategoriesAsync();
Components and Instances
const component = figma.createComponent();
component.addComponentProperty("Label", "TEXT", "Button");
component.addComponentProperty("Disabled", "BOOLEAN", false);
component.addComponentProperty("Icon", "INSTANCE_SWAP", iconId, {
preferredValues: [{ type: 'COMPONENT', key: starIcon.key }]
});
const instance = component.createInstance();
instance.setProperties({ 'Label#0:1': 'Submit' });
const main = await instance.getMainComponentAsync(); // ASYNC required
Data Storage
node.setPluginData('key', JSON.stringify({ value: 1 }));
const data = JSON.parse(node.getPluginData('key') || '{}');
await figma.clientStorage.setAsync('prefs', { theme: 'dark' });
const prefs = await figma.clientStorage.getAsync('prefs');
Images
const image = await figma.createImageAsync('https://example.com/image.png');
rect.fills = [{ type: 'IMAGE', imageHash: image.hash, scaleMode: 'FILL' }];
const bytes = await node.exportAsync({ format: 'PNG', constraint: { type: 'SCALE', value: 2 } });
Payments (Monetization)
Requires "payments" in manifest permissions.
const { status } = figma.payments;
if (status.type === 'UNPAID') {
await figma.payments.initiateCheckoutAsync({ interstitial: 'TRIAL_ENDED' });
} else if (status.type === 'PAID') { /* Full access */ }
const secondsSinceFirstRun = figma.payments.getUserFirstRanSecondsAgo();
UI Communication
figma.ui.postMessage({ type: 'data', items: [1, 2, 3] }); // code → UI
parent.postMessage({ pluginMessage: { type: 'action', data: 123 } }, '*'); // UI → code
Theme CSS Variables
With themeColors: true, detect dark mode via .figma-dark on <body>:
body { background: var(--figma-color-bg); color: var(--figma-color-text); }
.button { background: var(--figma-color-bg-brand); color: var(--figma-color-text-onbrand); }
Plugin UI Libraries
@create-figma-plugin/ui(v4+) — Best option. Preact components matching Figma UI3.figma-plugin-ds— Vanilla CSS/JS, older UI2 style.- Figma's UI3 Kit is a design file only (no code components).
Events
figma.on('run', ({ command, parameters }) => { });
figma.on('selectionchange', () => { });
figma.on('currentpagechange', () => { });
figma.on('documentchange', (event) => { /* event.documentChanges */ });
figma.on('drop', (event) => { return false; });
figma.on('stylechange', (event) => { });
figma.on('close', () => { /* SYNC ONLY */ });
figma.currentPage.on('nodechange', ({ nodeChanges }) => { }); // page-level
Performance
figma.skipInvisibleInstanceChildren = true; // ALWAYS enable
const texts = figma.currentPage.findAllWithCriteria({ types: ['TEXT'] });
const tagged = figma.currentPage.findAllWithCriteria({ pluginData: { keys: ['myKey'] } });
await page.loadAsync(); // load only needed pages
Critical Rules
- Always call
figma.closePlugin()— Plugin runs forever otherwise - Use
documentAccess: "dynamic-page"— Mandatory for all new plugins - Use async API methods — Sync versions are deprecated. Use
Asyncvariants. - Load fonts before text changes — Throws without loaded font
- Clone arrays before modifying — fills, effects, strokes are read-only
- No async in
closeevent — Must be synchronous - Declare network domains — CSP blocks undeclared domains
- RGB values are 0-1 — Not 0-255
- UI messages need
pluginMessagewrapper —{ pluginMessage: data } - Dev Mode plugins are read-only — Cannot create or modify nodes
- Codegen
generatehas 15s timeout — NoshowUI()inside it
References
- Full API Reference: See references/api-reference.md for node types (35), methods, properties, text API, components
- Variables API: See references/variables-api.md for design tokens — collections, modes, binding, scopes
- Editors & Codegen: See references/editors-codegen.md for FigJam, Slides, Buzz, Dev Mode, codegen, dev resources, annotations
- Publishing: See references/publishing.md for review, payments setup, distribution
Debugging
console.log('Debug:', value); // Plugins > Development > Open Console
figma.notify('Status message');
figma.notify('Error', { error: true, timeout: 5000 });
Recommended Agent Skills
Expand your agent's capabilities with these related and highly-rated skills.
agent-ops-spec
Manage specification documents in .agent/specs/. Use when user provides requirements, acceptance criteria, or feature descriptions that need to be tracked and validated against implementation.
agent-ops-state
Maintain .agent state files. Use at session start, after meaningful steps, and before concluding: read/update constitution/memory/focus/issues/baseline consistently.
agent-ops-spec
Manage specification documents in .agent/specs/. Use when user provides requirements, acceptance criteria, or feature descriptions that need to be tracked and validated against implementation.
agent-ops-testing
Test strategy, execution, and coverage analysis. Use when designing tests, running test suites, or analyzing test results beyond baseline checks.
agent-ops-testing
Test strategy, execution, and coverage analysis. Use when designing tests, running test suites, or analyzing test results beyond baseline checks.
agent-ops-state
Maintain .agent state files. Use at session start, after meaningful steps, and before concluding: read/update constitution/memory/focus/issues/baseline consistently.
Didn't find tool you were looking for?