### Install Dependencies
Source: https://github.com/figma/plugin-samples/blob/main/README.md
Install project dependencies for bundling with Webpack and React.
```bash
$ npm install
```
```bash
$ npm run build
```
--------------------------------
### Install Project Dependencies
Source: https://github.com/figma/plugin-samples/blob/main/README.md
Installs the necessary project dependencies, including Figma typings, for the sample plugins.
```bash
$ npm install
```
--------------------------------
### Install Dependencies and Compile Plugin (Bash)
Source: https://context7.com/figma/plugin-samples/llms.txt
Commands to install global TypeScript, repository dependencies, and compile a specific plugin. Also includes steps for plugins using bundlers like webpack.
```bash
# Install global TypeScript compiler and shared typings
npm install -g typescript
npm install # installs @figma/plugin-typings at repo root
# Compile a specific plugin (e.g. barchart)
cd barchart
tsc
# For plugins using a bundler (webpack / esbuild / vite):
cd webpack-react
npm install
npm run build
```
--------------------------------
### Install TypeScript Compiler
Source: https://github.com/figma/plugin-samples/blob/main/README.md
Installs the TypeScript compiler globally using npm. This is a prerequisite for compiling the plugin code.
```bash
$ npm install -g typescript
```
--------------------------------
### W3C Design Tokens Spec Example
Source: https://github.com/figma/plugin-samples/blob/main/variables-import-export/README.md
Illustrates the expected JSON structure for defining design tokens according to the W3C Design Tokens specification, including basic value types and aliases.
```json
{
"group name": {
"token name": {
"$value": 1234,
"$type": "number"
}
},
"alias name": {
"$value": "{group name.token name}"
}
}
```
--------------------------------
### Create Auto-Layout Frame Container (TypeScript)
Source: https://context7.com/figma/plugin-samples/llms.txt
Shows how to create a FrameNode, resize it, position it, and add child nodes. It also includes an example of configuring auto-layout properties like direction, padding, spacing, and sizing modes.
```typescript
// Create an 800×600 frame centered in the viewport, with a solid border
const frame = figma.createFrame();
frame.resizeWithoutConstraints(800, 600);
frame.x = figma.viewport.center.x - 400;
frame.y = figma.viewport.center.y - 300;
const border = figma.createRectangle();
frame.appendChild(border);
border.resizeWithoutConstraints(800, 600);
border.strokeAlign = 'INSIDE';
border.strokeWeight = 3;
border.fills = [];
border.strokes = [{ type: 'SOLID', color: { r: 0, g: 0, b: 0 } }];
border.constraints = { horizontal: 'STRETCH', vertical: 'STRETCH' };
// Auto-layout tally card example
const card = figma.createFrame();
card.cornerRadius = 8;
card.layoutMode = 'VERTICAL';
card.paddingLeft = card.paddingRight = card.paddingTop = card.paddingBottom = 16;
card.itemSpacing = 8;
card.primaryAxisSizingMode = card.counterAxisSizingMode = 'AUTO';
card.fills = [{ type: 'SOLID', color: { r: 1, g: 1, b: 1 } }];
card.strokes = [{ type: 'SOLID', color: { r: 0, g: 0, b: 0 } }];
card.strokeWeight = 2;
figma.currentPage.appendChild(card);
```
--------------------------------
### Figma Plugin Manifest Structure (JSON)
Source: https://context7.com/figma/plugin-samples/llms.txt
Example of a manifest.json file required for every Figma plugin. It declares metadata, API version, entry points, editor type, network access, and document access.
```json
{
"id": "bar-chart-sample",
"name": "Bar Chart Sample",
"api": "1.0.0",
"main": "code.js",
"ui": "ui.html",
"editorType": ["figma", "figjam"],
"networkAccess": {
"allowedDomains": ["https://fonts.googleapis.com"]
},
"documentAccess": "dynamic-page"
}
```
--------------------------------
### Add Drag Start Listener to Icons
Source: https://github.com/figma/plugin-samples/blob/main/icon-drag-and-drop-hosted/index.html
Attaches a 'dragstart' event listener to all elements with the class 'icon'. This listener sets the dataTransfer object with the SVG content of the dragged icon, preparing it for drag and drop operations.
```javascript
const icons = document.getElementsByClassName('icon');
for (const icon of icons) {
icon.addEventListener('dragstart', (e) => {
e.dataTransfer.setData("image/svg+xml", e.target.innerHTML);
})
}
```
--------------------------------
### Plugin Parameters and Quick Actions
Source: https://context7.com/figma/plugin-samples/llms.txt
Details how to handle user input for Quick Actions using `figma.parameters.on('input', ...)` for suggestions and `figma.on('run', ...)` for plugin execution.
```APIDOC
## Plugin Parameters
### `figma.parameters.on('input', ...)` / `figma.on('run', ...)` — Quick Actions with suggestions
Register an `input` handler to provide autocomplete `setSuggestions()` or `setError()` on each keystroke. The `run` event fires when the user confirms all parameters and the plugin executes.
### Example Usage:
```typescript
// Resizer plugin: accept width/height or a scale factor via Quick Actions
figma.parameters.on('input', ({ query, key, result }: ParameterInputEvent) => {
if (figma.currentPage.selection.length === 0) {
result.setError('Please select one or more nodes first');
return;
}
switch (key) {
case 'width':
validateNumber(query, result, ['640', '800', '1024', '1280']);
break;
case 'height':
validateNumber(query, result, ['480', '600', '720', '960']);
break;
case 'scale':
validateNumber(query, result);
break;
}
});
figma.on('run', ({ command, parameters }: RunEvent) => {
if (!parameters) return;
if (command === 'relative') {
const scale = parseFloat(parameters.scale as string);
for (const node of figma.currentPage.selection) {
if ('rescale' in node) node.rescale(scale);
}
} else {
const w = parseInt(parameters.width as string);
const h = parseInt(parameters.height as string);
for (const node of figma.currentPage.selection) {
if ('resize' in node) node.resize(w, h);
}
}
figma.closePlugin();
});
function validateNumber(query: string, result: SuggestionResults, completions?: string[]) {
if (query === '') { result.setSuggestions(completions ?? []); return; }
if (!Number.isFinite(Number(query))) { result.setError('Please enter a numeric value'); return; }
if (Number(query) <= 0) { result.setError('Must be larger than 0'); return; }
result.setSuggestions([query, ...(completions ?? []).filter(s => s.includes(query) && s !== query)]);
}
```
```
--------------------------------
### Serve HTML Locally with Python
Source: https://github.com/figma/plugin-samples/blob/main/icon-drag-and-drop-hosted/README.md
Use this command to serve the index.html file locally on port 4004 for development.
```bash
python3 -m http.server 4004
```
--------------------------------
### Create and Style Rectangles (TypeScript)
Source: https://context7.com/figma/plugin-samples/llms.txt
Demonstrates creating multiple rectangle nodes, styling them with solid fills, positioning them, appending them to the current page, selecting them, and zooming the viewport to fit.
```typescript
// Create 5 orange rectangles spaced 150px apart, select them, and zoom to fit
const nodes: SceneNode[] = [];
for (let i = 0; i < 5; i++) {
const rect = figma.createRectangle();
rect.x = i * 150;
rect.fills = [{ type: 'SOLID', color: { r: 1, g: 0.5, b: 0 } }];
figma.currentPage.appendChild(rect);
nodes.push(rect);
}
figma.currentPage.selection = nodes;
figma.viewport.scrollAndZoomIntoView(nodes);
figma.closePlugin();
```
--------------------------------
### Initialize and Handle Plugin Messages in JavaScript
Source: https://github.com/figma/plugin-samples/blob/main/snippet-saver/ui.html
Sets up event listeners for initializing the plugin and handling messages from the Figma plugin host. It parses incoming plugin data and updates the UI accordingly.
```javascript
const LANGUAGES = [
"BASH",
"CPP",
"CSS",
"GO",
"GRAPHQL",
"HTML",
"JAVASCRIPT",
"JSON",
"KOTLIN",
"PLAINTEXT",
"PYTHON",
"RUBY",
"RUST",
"SQL",
"SWIFT",
"TYPESCRIPT",
];
const LANGUAGE_OPTIONS = LANGUAGES.map( (l) => `` ).join("");
const main = document.querySelector("main");
let snippetList = [];
parent.postMessage({ pluginMessage: { type: "INITIALIZE" } }, "*");
window.onmessage = (e) => {
const data = e.data.pluginMessage;
if (data.type === "SELECTION") {
handleSelectionChange(data);
} else {
console.log(data);
}
};
```
--------------------------------
### Figma Plugin Parameters with Input Suggestions
Source: https://context7.com/figma/plugin-samples/llms.txt
Implement Quick Actions with `figma.parameters.on('input', ...)` for real-time suggestions and error handling. The `figma.on('run', ...)` event fires when the user confirms parameters.
```typescript
figma.parameters.on('input', ({ query, key, result }: ParameterInputEvent) => {
if (figma.currentPage.selection.length === 0) {
result.setError('Please select one or more nodes first');
return;
}
switch (key) {
case 'width':
validateNumber(query, result, ['640', '800', '1024', '1280']);
break;
case 'height':
validateNumber(query, result, ['480', '600', '720', '960']);
break;
case 'scale':
validateNumber(query, result);
break;
}
});
figma.on('run', ({ command, parameters }: RunEvent) => {
if (!parameters) return;
if (command === 'relative') {
const scale = parseFloat(parameters.scale as string);
for (const node of figma.currentPage.selection) {
if ('rescale' in node) node.rescale(scale);
}
} else {
const w = parseInt(parameters.width as string);
const h = parseInt(parameters.height as string);
for (const node of figma.currentPage.selection) {
if ('resize' in node) node.resize(w, h);
}
}
figma.closePlugin();
});
function validateNumber(query: string, result: SuggestionResults, completions?: string[]) {
if (query === '') { result.setSuggestions(completions ?? []); return; }
if (!Number.isFinite(Number(query))) { result.setError('Please enter a numeric value'); return; }
if (Number(query) <= 0) { result.setError('Must be larger than 0'); return; }
result.setSuggestions([query, ...(completions ?? []).filter(s => s.includes(query) && s !== query)]);
}
```
--------------------------------
### Node Search and Tree Traversal
Source: https://context7.com/figma/plugin-samples/llms.txt
Demonstrates how to find nodes on the current page using `findAll` and how to implement a non-blocking tree walker for large documents.
```APIDOC
## `figma.currentPage.findAll()` / `walkTree` generator — Node search patterns
`findAll(predicate)` performs a depth-first search of the entire page sub-tree to return all matching nodes. For large documents that might block the UI thread, a generator-based walker with `setTimeout` can be used to yield control periodically.
### Example Usage:
```typescript
// Find all STAMP nodes on the current page (FigJam vote-tally pattern)
const stamps = figma.currentPage.findAll(node => node.type === 'STAMP');
const stickies = figma.currentPage.findChildren(node => node.type === 'STICKY');
// Non-blocking text search using a generator and setTimeout
function* walkTree(node: BaseNode) {
yield node;
if ('children' in node) {
for (const child of node.children) yield* walkTree(child);
}
}
function searchFor(query: string) {
const walker = walkTree(figma.currentPage);
let timer: ReturnType;
function processChunk() {
const results: string[] = [];
let count = 0;
let done = true;
let res: IteratorResult;
while (!(res = walker.next()).done) {
const node = res.value;
if (node.type === 'TEXT' && node.characters.toLowerCase().includes(query)) {
results.push(node.id);
}
if (++count === 1000) { // yield every 1000 nodes to keep UI responsive
done = false;
timer = setTimeout(processChunk, 20);
break;
}
}
figma.ui.postMessage({ query, results, done });
}
processChunk();
}
```
```
--------------------------------
### Import W3C Design Tokens with `importJSONFile()`
Source: https://context7.com/figma/plugin-samples/llms.txt
Parses a token JSON file, creates a variable collection, and resolves alias references. Ensure `parseColor` is defined elsewhere.
```typescript
// Trigger from the UI after the user selects a file
figma.ui.onmessage = (e) => {
if (e.type === 'IMPORT') {
importJSONFile({ fileName: e.fileName, body: e.body });
}
};
function importJSONFile({ fileName, body }: { fileName: string; body: string }) {
const json = JSON.parse(body);
const collection = figma.variables.createVariableCollection(fileName);
const modeId = collection.modes[0].modeId;
const tokens: Record = {};
const aliases: Record = {};
// Recursively walk the token tree; defer aliases for a second pass
function traverse(key: string, obj: Record, type?: string) {
type = type || (obj.$type as string);
if (key.startsWith('$')) return;
if (obj.$value !== undefined) {
const val = String(obj.$value).trim();
if (val.startsWith('{')) {
const valueKey = val.replace(/[{}]/g, '').replace(/\./g, '/');
aliases[key] = { key, valueKey };
} else if (type === 'color') {
const token = figma.variables.createVariable(key, collection, 'COLOR');
token.setValueForMode(modeId, parseColor(val)); // parseColor not shown
tokens[key] = token;
} else if (type === 'number') {
const token = figma.variables.createVariable(key, collection, 'FLOAT');
token.setValueForMode(modeId, obj.$value as number);
tokens[key] = token;
}
} else {
for (const [k, v] of Object.entries(obj)) {
if (!k.startsWith('$')) traverse(`${key}/${k}`, v as Record, type);
}
}
}
for (const [key, val] of Object.entries(json)) traverse(key, val as Record);
// Resolve aliases
for (const { key, valueKey } of Object.values(aliases)) {
if (tokens[valueKey]) {
const alias = figma.variables.createVariable(key, collection, tokens[valueKey].resolvedType);
alias.setValueForMode(modeId, { type: 'VARIABLE_ALIAS', id: tokens[valueKey].id });
}
}
}
```
--------------------------------
### Build Plugin Files with TypeScript
Source: https://github.com/figma/plugin-samples/blob/main/icon-drag-and-drop-hosted/README.md
Compile TypeScript files to JavaScript for the plugin to function.
```bash
tsc
```
--------------------------------
### Basic CSS for Icon Grid and Icons
Source: https://github.com/figma/plugin-samples/blob/main/icon-drag-and-drop-hosted/index.html
Provides basic styling for the icon grid and individual icons, including layout, sizing, and hover effects. Ensures icons are draggable with a 'grab' cursor.
```css
body { background-color: var(--figma-color-bg); color: var(--figma-color-text); }
.icons-grid { display: flex; flex-wrap: wrap; }
.icon { border-radius: 4px; cursor:grab; height:24px; margin: 4px; padding: 4px; width:24px; }
.icon:hover { background-color: #ddd; }
```
--------------------------------
### Listen for Document Changes with figma.on('documentchange', ...)
Source: https://context7.com/figma/plugin-samples/llms.txt
Observe real-time document mutations like creates, deletes, and property changes. The event provides details on the origin, type, and affected node. Ensure `figma.loadAllPagesAsync()` is called to monitor changes on all pages.
```typescript
figma.showUI(__html__, { height: 600, width: 600 });
async function initialize() {
await figma.loadAllPagesAsync(); // required to observe changes on non-current pages
figma.on('documentchange', (event) => {
const messages = event.documentChanges.map(change => {
const { origin, type } = change;
const parts = [origin, type];
if (type === 'PROPERTY_CHANGE') {
parts.push(change.node.type, change.properties.join(', '));
} else if (type === 'STYLE_PROPERTY_CHANGE') {
parts.push(change.style?.name ?? '', change.properties.join(', '));
} else if (type !== 'STYLE_CREATE' && type !== 'STYLE_DELETE') {
parts.push(change.node.type);
}
return parts.join(' ');
// Example output: "LOCAL PROPERTY_CHANGE RECTANGLE fills, opacity"
});
figma.ui.postMessage(messages, { origin: '*' });
});
}
void initialize();
```
--------------------------------
### Asynchronous Node and Page Navigation
Source: https://context7.com/figma/plugin-samples/llms.txt
Provides methods for asynchronously fetching nodes by ID and switching to different pages, enabling navigation within the Figma document.
```APIDOC
## `figma.getNodeByIdAsync()` / `figma.setCurrentPageAsync()` — Node and page navigation
Asynchronously fetch any node by its stable ID string. Switch to a different page before zooming into a cross-page node.
### Example Usage:
```typescript
// Navigate to a layer or page by ID (go-to plugin pattern)
async function goToNode(nodeId: string) {
const node = await figma.getNodeByIdAsync(nodeId);
if (!node) {
figma.notify('Node not found');
figma.closePlugin();
return;
}
if (node.type === 'PAGE') {
await figma.setCurrentPageAsync(node as PageNode);
} else {
// Walk up the tree to find which page this node lives on
let parent = node.parent;
while (parent && parent.type !== 'PAGE') parent = parent.parent;
if (parent) await figma.setCurrentPageAsync(parent as PageNode);
figma.viewport.scrollAndZoomIntoView([node as SceneNode]);
figma.currentPage.selection = [node as SceneNode];
}
figma.closePlugin();
}
```
```
--------------------------------
### figma.on('documentchange', ...)
Source: https://context7.com/figma/plugin-samples/llms.txt
Listen for real-time document mutations in Figma. This event handler captures creates, deletes, and property changes, providing details about the origin and type of each change.
```APIDOC
## `figma.on('documentchange', ...)` — React to real-time document mutations
### Description
Listen for all creates, deletes, and property changes on the document. The event delivers an array of `DocumentChange` objects describing what changed, which node was affected, and whether the change originated from the user, a remote collaborator, or a plugin.
### Usage
```typescript
figma.showUI(__html__, { height: 600, width: 600 });
async function initialize() {
await figma.loadAllPagesAsync(); // required to observe changes on non-current pages
figma.on('documentchange', (event) => {
const messages = event.documentChanges.map(change => {
const { origin, type } = change;
const parts = [origin, type];
if (type === 'PROPERTY_CHANGE') {
parts.push(change.node.type, change.properties.join(', '));
} else if (type === 'STYLE_PROPERTY_CHANGE') {
parts.push(change.style?.name ?? '', change.properties.join(', '));
} else if (type !== 'STYLE_CREATE' && type !== 'STYLE_DELETE') {
parts.push(change.node.type);
}
return parts.join(' ');
// Example output: "LOCAL PROPERTY_CHANGE RECTANGLE fills, opacity"
});
figma.ui.postMessage(messages, { origin: '*' });
});
}
void initialize();
```
```
--------------------------------
### figma.getLocalPaintStylesAsync()
Source: https://context7.com/figma/plugin-samples/llms.txt
Convert local paint styles into variable tokens. This function reads all paint styles, deduplicates them by color and opacity, and creates corresponding variable collections.
```APIDOC
## `figma.getLocalPaintStylesAsync()` — Convert paint styles to variable tokens
### Description
Reads all local paint styles, deduplicates by hex + opacity, and creates a `Style Tokens` variable collection. Styles that share a color value are aliased to a single parent variable in a companion `Style Tokens: Aliased` collection.
### Usage
```typescript
async function initialize() {
const styles = await figma.getLocalPaintStylesAsync();
// Build a map: hex-opacity → { color, tokens[] }
const tokenDataMap: Record = {};
for (const style of styles) {
const paints = style.paints.filter(p => p.visible && p.type === 'SOLID');
if (paints.length !== 1) continue;
const { color, opacity = 1, blendMode } = paints[0] as SolidPaint;
if (blendMode !== 'NORMAL') continue;
const hex = rgbToHex(color);
const id = `${hex}-${opacity}`;
tokenDataMap[id] ??= { color, hex, opacity, tokens: [] };
tokenDataMap[id].tokens.push(style.name);
}
const collection = figma.variables.createVariableCollection('Style Tokens');
const modeId = collection.modes[0].modeId;
for (const { color, opacity, tokens } of Object.values(tokenDataMap)) {
for (const name of tokens) {
const token = figma.variables.createVariable(name, collection, 'COLOR');
token.setValueForMode(modeId, { r: color.r, g: color.g, b: color.b, a: opacity });
}
}
figma.closePlugin();
}
void initialize();
```
```
--------------------------------
### Plugin: Draw Bar Chart from UI Input
Source: https://context7.com/figma/plugin-samples/llms.txt
This TypeScript code runs in the plugin sandbox. It receives an array of numbers from the UI, loads a font, creates a frame, and draws a bar chart based on the input numbers. The plugin closes after drawing.
```typescript
figma.showUI(__html__);
figma.ui.onmessage = async (numbers: number[]) => {
await figma.loadFontAsync({ family: 'Inter', style: 'Regular' });
const frameWidth = 800, frameHeight = 600;
const frame = figma.createFrame();
frame.resizeWithoutConstraints(frameWidth, frameHeight);
frame.x = figma.viewport.center.x - frameWidth / 2;
frame.y = figma.viewport.center.y - frameHeight / 2;
const min = numbers.reduce((a, b) => Math.min(a, b), 0);
const max = numbers.reduce((a, b) => Math.max(a, b), 0);
for (let i = 0; i < numbers.length; i++) {
const num = numbers[i];
const left = 25 + 750 * (i + 0.25) / numbers.length;
const right = 25 + 750 * (i + 0.75) / numbers.length;
const top = 50 + 550 - 550 * (Math.max(0, num) - min) / (max - min);
const bottom = 50 + 550 - 550 * (Math.min(0, num) - min) / (max - min);
const col = figma.createRectangle();
frame.appendChild(col);
col.x = left; col.y = top;
col.resizeWithoutConstraints(right - left, bottom - top);
col.fills = [{ type: 'SOLID', color: { r: 1, g: 0, b: 0 } }];
}
figma.closePlugin();
};
```
--------------------------------
### Fetch External Data for Parameter Suggestions
Source: https://context7.com/figma/plugin-samples/llms.txt
Use an invisible UI iframe to fetch data from external APIs for parameter suggestions. Network requests are not allowed directly in the plugin sandbox.
```typescript
// manifest.json for a parameter plugin that queries an external API
// {
// "parameters": [{ "name": "Country", "key": "country", "description": "Country name" }],
// "networkAccess": { "allowedDomains": ["https://restcountries.com"] }
// }
figma.parameters.on('input', ({ key, query, result }: ParameterInputEvent) => {
if (key === 'country') {
// Delegate the network call to the UI iframe
figma.showUI(__html__, { visible: false });
figma.ui.postMessage({ type: 'FETCH_CAPITALS', query });
figma.ui.onmessage = (suggestions: Array<{ name: string; data: unknown }>) => {
result.setSuggestions(suggestions);
};
}
});
figma.on('run', ({ parameters }: RunEvent) => {
const capital = (parameters!['country'] as { capital: string }).capital;
figma.notify(`Capital: ${capital}`);
figma.closePlugin();
});
```
--------------------------------
### Handle Multi-Context Plugins with `figma.editorType` and `figma.mode`
Source: https://context7.com/figma/plugin-samples/llms.txt
Branches plugin behavior based on the current editor context (design, Dev Mode) and mode (inspect, codegen).
```typescript
// dev-mode/code.js — handle all three contexts
if (figma.editorType === 'dev') {
if (figma.mode === 'inspect') {
figma.showUI('