Home › Figma plugins › Figma plugin CLAUDE.md
Figma plugin CLAUDE.md
CLAUDE.md for Figma plugins in TypeScript: main thread vs UI iframe, message passing, dynamic page loading with async APIs, fonts, manifest permissions, and testing.
Claude Code (CLAUDE.md)Figma plugins
How to use
Save this as CLAUDE.md in your repository root, commit it, and your agent will pick it up automatically. Edit the commands to match your project.
Note: Review and test before use. Provided as is, without warranty. See Terms.
CLAUDE.mdDownload # CLAUDE.md: Figma plugin
## Commands
- `npm run build` (bundles `code.ts` and the UI) · `npm run watch` · `npm run typecheck` · `npm run lint` if present.
- Test by importing `manifest.json` in the Figma desktop app (Plugins → Development). You can't run Figma here; say what needs a manual check.
## Architecture
- Two contexts: the main thread (`code.ts`) has the `figma` API but no DOM or browser APIs; the UI (`ui.html`/React in an iframe) has the DOM but no `figma` API. They talk only through `figma.ui.postMessage(msg)` ↔ `window.onmessage` and `parent.postMessage({ pluginMessage: msg }, '*')` ↔ `figma.ui.onmessage`.
- Define every message as a typed discriminated union in `src/messages.ts`, shared by both sides. Validate incoming messages; ignore unknown types.
- Keep `@figma/plugin-typings` current and typecheck both bundles.
## Figma API rules
- The manifest uses `"documentAccess": "dynamic-page"`: use async APIs (`figma.getNodeByIdAsync`, `page.loadAsync()`, `figma.loadAllPagesAsync()` only when truly needed). Don't touch other pages' nodes without loading them.
- Load fonts before changing text: `await figma.loadFontAsync(node.fontName as FontName)`; handle `figma.mixed` font names by loading each segment's font.
- Batch edits and keep them scoped to the selection or a known node; never iterate the whole document with `findAll` when `findAllWithCriteria` or the selection works.
- Wrap work in try/catch and finish with `figma.notify(...)` for success or failure and `figma.closePlugin()` for one-shot commands.
- Use `figma.clientStorage` for user preferences; use `setPluginData`/`setSharedPluginData` (with a namespace) for data attached to nodes. Never store secrets in plugin data.
## Manifest
Declare `networkAccess.allowedDomains` precisely (no `*` unless justified in `reasoning`), the `editorType` actually supported (`figma`, `figjam`, `dev`), and `capabilities` only when used. Don't change the plugin `id`.