# How to Build Figma Integration Plugins with the use_figma Capability

> Learn to build Figma integration plugins using the OpenAI plugins repository. Execute JavaScript in Figma files with the use_figma capability and retrieve JSON results.

- Repository: [OpenAI/plugins](https://github.com/openai/plugins)
- Tags: how-to-guide
- Published: 2026-06-07

---

**You build Figma integration plugins by loading the `figma-use` skill and sending JavaScript code blocks to the `use_figma` tool, which executes them inside Figma files and returns only JSON-serialized return values.**

The `use_figma` capability in the [openai/plugins](https://github.com/openai/plugins) repository provides a **Codex-backed plugin** architecture that wraps the official Figma Plugin API. This integration enables AI assistants to programmatically create nodes, edit design systems, and manipulate files through a secure runtime environment. Understanding the three-layer architecture and strict execution rules is essential for writing reliable scripts that interact with Figma files.

## Architecture of the use_figma Capability

The Figma integration is structured as a multi-layered system that separates configuration, skill definitions, and runtime execution.

### Plugin Manifest and Skill Structure

The first layer resides in [`plugins/figma/.codex-plugin/plugin.json`](https://github.com/openai/plugins/blob/main/plugins/figma/.codex-plugin/plugin.json), which defines the plugin to Codex and specifies which app to load. The second layer consists of **skill packages** located in `plugins/figma/skills/figma-use/`. Each skill is a self-contained bundle containing:

- [`SKILL.md`](https://github.com/openai/plugins/blob/main/SKILL.md) – Metadata and execution rules
- `references/` – Documentation and API references
- Optional `agents/`, `assets/`, and `scripts/` directories

The `figma-use` skill serves as the core entry point for any script that needs to run JavaScript inside a Figma file.

### Runtime Execution Model

When the assistant calls `use_figma`, the runtime injects the supplied script into a temporary plugin running within the target file. The execution follows five strict stages:

1. **Skill Loading** – The assistant must load the `figma-use` skill before any call to set up the environment.
2. **Tool Invocation** – The assistant sends a JSON-encoded block specifying `"tool": "use_figma"` and the JavaScript code.
3. **Automatic Wrapping** – The runtime adds an `async` wrapper and injects top-level `await`. You must not wrap your code in an IIFE.
4. **Return-Only Communication** – Only the final `return` value is serialized and sent back; `console.log` and `figma.notify` are ignored.
5. **Atomic Execution** – If the script throws an error, the file remains untouched with no partial changes applied.

## Critical Rules for use_figma Scripts

Scripts executing under the `use_figma` capability must adhere to specific constraints to ensure safe file manipulation.

### Communication via Return Values

Every script must end with a top-level `return` statement containing a **JSON-serializable object**. You cannot rely on standard output or UI notifications to convey information back to the assistant.

```javascript
// Correct: Returns data explicitly
const rect = figma.createRectangle();
return { createdNodeIds: [rect.id] };

// Incorrect: console.log is silently discarded
console.log("Created rectangle");

```

### Incremental Workflow Constraints

Limit each `use_figma` call to **at most 10 logical operations**. Split larger workflows into multiple sequential calls to avoid timeouts and simplify debugging. Each call may touch only one page, so page switching requires separate invocations.

### Error Handling and Atomicity

All changes within a single call are atomic. If any part of the script throws, the entire operation rolls back, leaving the Figma file unchanged. This prevents corrupted states during complex multi-node operations.

## Core Concepts for Figma File Manipulation

Mastering these patterns ensures your scripts handle Figma's asynchronous API and node hierarchy correctly.

### Page Navigation and Context

`figma.currentPage` always starts on the first page of the file. Switch pages only with `await figma.setCurrentPageAsync(page)` and never inside loops.

```javascript
// Find and switch to the Design System page
const targetPage = figma.root.children.find(p => p.name === "Design System");
await figma.setCurrentPageAsync(targetPage);
return { pageId: targetPage.id };

```

### The Canonical Text-Edit Recipe

Text mutations require explicit font loading to prevent "Cannot write to node with unloaded font" errors. Always follow this sequence:

```javascript
const txt = await figma.getNodeByIdAsync(textId);
if (txt.type !== "TEXT") throw new Error("Node is not a text node");

// Load fonts before mutation
await txt.loadFontAsync();
txt.characters = "Updated text";

return { mutatedNodeIds: [txt.id] };

```

### Auto-Layout and Node Creation

Always create containers with `figma.createAutoLayout` before adding children. This guarantees that `layoutSizingHorizontal/Vertical = 'FILL'` works correctly. The skill provides helper methods like `node.query` and `node.set` to reduce boilerplate.

## Practical use_figma Code Examples

These snippets demonstrate patterns from [`plugins/figma/skills/figma-use/SKILL.md`](https://github.com/openai/plugins/blob/main/plugins/figma/skills/figma-use/SKILL.md) that handle common tasks.

### Creating Rectangles and Returning Node IDs

Create a red rectangle at specific coordinates and return its identifier for future reference:

```javascript
const rect = figma.createRectangle();
rect.resize(200, 120);
rect.fills = [{type: "SOLID", color: {r: 1, g: 0, b: 0}}];
rect.x = 100;
rect.y = 100;
figma.currentPage.appendChild(rect);

return { createdNodeIds: [rect.id] };

```

### Querying Nodes with CSS-like Selectors

Use the `node.query` API to find and batch-modify nodes matching specific patterns:

```javascript
// Find all rectangles ending with "Button" and reduce opacity
const buttons = figma.currentPage.query('RECTANGLE[name$=Button]');
buttons.set({ opacity: 0.6 });

return { mutatedNodeIds: buttons.map(n => n.id) };

```

### Working with Components and Instances

Switch to a non-default page and instantiate a component:

```javascript
const targetPage = figma.root.children.find(p => p.name === "Design System");
await figma.setCurrentPageAsync(targetPage);

const container = figma.createAutoLayout("VERTICAL", { name: "Cards", itemSpacing: 12 });
figma.currentPage.appendChild(container);

const comp = await figma.getNodeByIdAsync("123:456");
const instance = comp.createInstance();
container.appendChild(instance);

return {
  createdNodeIds: [container.id, instance.id],
  pageId: targetPage.id
};

```

### Capturing Inline Screenshots

Generate PNG previews of specific frames after modifications:

```javascript
const frame = figma.currentPage.query('FRAME[name=Header]').first();
await frame.screenshot({ scale: 2 });

return { screenshotNodeId: frame.id };

```

## Essential Reference Files in openai/plugins

Consult these source files for detailed specifications and troubleshooting:

- **[`plugins/figma/.codex-plugin/plugin.json`](https://github.com/openai/plugins/blob/main/plugins/figma/.codex-plugin/plugin.json)** – Plugin manifest defining the Figma app integration
- **[`plugins/figma/skills/figma-use/SKILL.md`](https://github.com/openai/plugins/blob/main/plugins/figma/skills/figma-use/SKILL.md)** – Core skill definition, execution rules, and best-practice checklist
- **[`plugins/figma/skills/figma-use/references/gotchas.md`](https://github.com/openai/plugins/blob/main/plugins/figma/skills/figma-use/references/gotchas.md)** – Common failure modes and correct/incorrect code comparisons
- **[`plugins/figma/skills/figma-use/references/api-reference.md`](https://github.com/openai/plugins/blob/main/plugins/figma/skills/figma-use/references/api-reference.md)** – Full auto-generated typings for the Plugin API
- **[`plugins/figma/skills/figma-generate-design/SKILL.md`](https://github.com/openai/plugins/blob/main/plugins/figma/skills/figma-generate-design/SKILL.md)** – Multi-page screen generation skill (optional companion to `figma-use`)
- **[`plugins/figma/skills/figma-generate-library/SKILL.md`](https://github.com/openai/plugins/blob/main/plugins/figma/skills/figma-generate-library/SKILL.md)** – Component library and token binding generation skill

## Summary

- Load the **`figma-use`** skill before any `use_figma` invocation to initialize the execution environment.
- Use **top-level `await`** and a single `return` statement; never wrap code in IIFEs or use `console.log` for output.
- Return **node IDs** for all created or mutated elements to enable subsequent script references.
- Follow the **canonical text-edit recipe** (`loadFontAsync` before mutation) to prevent font loading errors.
- Limit each call to **10 logical operations** and split workflows across multiple `use_figma` calls for reliability.
- Switch pages only via **`await figma.setCurrentPageAsync(page)`** and verify with returned metadata or screenshots.

## Frequently Asked Questions

### What is the use_figma capability?

The `use_figma` capability is a tool in the openai/plugins repository that allows AI assistants to execute JavaScript inside Figma files through a controlled runtime. It acts as a bridge between Codex and the Figma Plugin API, automatically handling async execution and returning only JSON-serialized data from the script's return statement.

### How do I handle fonts when editing text in Figma via use_figma?

You must call `await txt.loadFontAsync()` before modifying the `characters` property of any text node. This loads all font variants used by the node and prevents write errors. The mutation must occur after the await resolves, as demonstrated in the canonical text-edit recipe in [`SKILL.md`](https://github.com/openai/plugins/blob/main/SKILL.md).

### Why is my use_figma script timing out?

Scripts timeout when they exceed execution limits or perform too many operations in a single call. Keep each invocation under **10 logical operations**, avoid heavy computations, and split complex workflows into multiple sequential `use_figma` calls. Ensure you are not creating infinite loops or performing blocking synchronous operations.

### Can I use console.log for debugging inside use_figma scripts?

No. The runtime ignores `console.log`, `figma.notify`, and `figma.closePlugin`. All communication must flow through the `return` statement. For debugging, return intermediate state objects or use the `node.screenshot()` method to capture visual confirmation of the file state.