How to Use the use_figma Tool with the Figma Plugin API

The use_figma tool enables AI assistants to execute JavaScript directly inside Figma files by loading the figma-use skill, wrapping code in an async harness, and requiring a JSON return value, with all operations executing atomically so errors roll back changes.

The use_figma tool in the openai/plugins repository serves as a bridge between AI assistants and the Figma design environment. By leveraging the official Figma Plugin API, this tool allows programmatic manipulation of design files through JavaScript execution. Understanding how to structure scripts, handle page navigation, and manage data persistence is essential for building reliable automation workflows.

Loading the Mandatory figma-use Skill

Every invocation of the use_figma tool requires loading the figma-use skill first. According to plugins/figma/skills/figma-use/SKILL.md, omitting this prerequisite causes hard-to-debug failures. When calling the tool, always include skillNames: "figma-use" in the request parameters.

Script Structure and Execution Model

The use_figma tool wraps your JavaScript in an async function automatically, allowing top-level await without manual IIFE wrapping. However, strict rules govern how scripts communicate results and handle side effects.

The Return Statement Requirement

Scripts must use a return statement to send data back to the assistant. Methods like console.log, figma.closePlugin(), and figma.notify() are ignored or throw "not implemented" errors. Always return a JSON-serializable object containing node IDs, counts, or status information.

Atomic Execution Guarantees

Execution is atomic: if your script throws an error, no changes are applied to the file. The error message returns to the assistant for correction, leaving the document unchanged. This safety mechanism prevents partial modifications that could corrupt the design.

Complete Workflow Example

The following script demonstrates the canonical pattern for use_figma calls, as defined in plugins/figma/skills/figma-use/SKILL.md:

// 1️⃣ Load the skill (handled by the assistant)
//    skillNames: "figma-use"

// 2️⃣ Switch to the target page (must be the first operation)
const targetPage = figma.root.children.find(p => p.name === "Landing");
await figma.setCurrentPageAsync(targetPage);

// 3️⃣ Create a frame with auto-layout
const frame = figma.createAutoLayout('VERTICAL', { name: "Card", itemSpacing: 12 });
targetPage.appendChild(frame);

// 4️⃣ Add a rectangle fill
const rect = figma.createRectangle();
rect.resize(300, 180);
rect.fills = [{type: "SOLID", color: {r: 0.1, g: 0.6, b: 0.9}}];
frame.appendChild(rect);

// 5️⃣ Add a text node (must load the font first)
await figma.loadFontAsync({family: "Inter", style: "Bold"});
const txt = figma.createText();
txt.characters = "Hello Figma";
txt.fontSize = 24;
txt.fills = [{type: "SOLID", color: {r: 1, g: 1, b: 1}}];
frame.appendChild(txt);

// 6️⃣ Take an inline screenshot of the finished card
await frame.screenshot();

// 7️⃣ Return IDs so the assistant can reference them later
return { createdNodeIds: [frame.id, rect.id, txt.id] };

This example illustrates several critical rules: using await figma.setCurrentPageAsync(page) for navigation, loading fonts before text manipulation, and returning serializable objects with node IDs.

The figma.currentPage property resets on every use_figma call. To work on specific pages, you must explicitly switch contexts using the asynchronous setter.

Use await figma.setCurrentPageAsync(page) instead of the synchronous setter, which is unsupported in this environment. Always perform page switching as the first operation in your script.

Creating Nodes and Auto-Layout

When constructing UI elements, prefer figma.createAutoLayout() for container frames. This method accepts direction parameters ('VERTICAL' or 'HORIZONTAL') and configuration options like itemSpacing.

Critical constraint: For child nodes to use layoutSizingHorizontal or layoutSizingVertical values of "FILL", the parent must already be an auto-layout frame before appending children.

const frame = figma.createAutoLayout('VERTICAL', { name: "Card", itemSpacing: 12 });
const rect = figma.createRectangle();
rect.layoutSizingHorizontal = "FILL"; // Works because frame is auto-layout
frame.appendChild(rect);

Data Persistence and Variable Scoping

The use_figma environment supports getSharedPluginData and setSharedPluginData for storing data accessible across plugins, but does not support getPluginData or setPluginData.

When creating variables, always explicitly set variable.scopes. The default ALL_SCOPES value pollutes every property picker in the UI, so narrow scoping is essential. Reference plugins/figma/skills/figma-use/references/variable-patterns.md for detailed scoping strategies.

Querying Nodes and Capturing Screenshots

Instead of verbose findAll loops, use the node.query(selector) method for CSS-like searches within the node tree.

For visual feedback, call await node.screenshot() to generate inline PNG images directly in the response. This avoids separate get_screenshot calls and provides immediate visual confirmation of changes.

const buttons = frame.query(".button-primary");
const screenshot = await frame.screenshot();

Incremental Workflow Constraints

Keep each use_figma call to 10 or fewer logical operations (creation, setting properties, and parenting). Split larger jobs into multiple calls and validate after each step. Return comprehensive ID lists in your payload to enable subsequent operations.

Key Reference Documentation

The following files in the openai/plugins repository provide authoritative documentation:

Summary

  • Always load the skill: Include skillNames: "figma-use" before calling use_figma
  • Return data explicitly: Use return statements with JSON-serializable objects; avoid console.log and figma.notify()
  • Handle pages asynchronously: Use await figma.setCurrentPageAsync(page) to switch contexts
  • Leverage auto-layout: Create auto-layout frames before appending children that use "FILL" sizing
  • Execute atomically: Script failures roll back all changes, preventing file corruption
  • Query efficiently: Use node.query(selector) instead of manual traversal loops
  • Capture inline screenshots: Call await node.screenshot() for immediate visual feedback

Frequently Asked Questions

What happens if my use_figma script throws an error?

If your script throws an error, no changes are applied to the Figma file. The execution is atomic, meaning the entire operation rolls back if any part fails. The error message returns to the assistant, allowing you to fix the script and retry without leaving the document in a partial state.

Can I use console.log or figma.notify() for debugging?

No. In the use_figma sandbox environment, console.log statements are ignored and figma.notify() throws a "not implemented" error. You must use the return statement to communicate debug information or status back to the assistant.

How do I switch between pages in a Figma file?

Call await figma.setCurrentPageAsync(page) with the target page node. The synchronous figma.currentPage setter is unsupported. Because figma.currentPage resets on every tool invocation, always perform page switching as the first operation in your script. Find target pages by searching figma.root.children by name or ID.

What is the difference between shared plugin data and regular plugin data?

The use_figma tool supports getSharedPluginData and setSharedPluginData, which store data accessible across different plugins. However, it does not support getPluginData or setPluginData, which store data private to a specific plugin instance. Use the shared variants for all data persistence needs.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →