My first Figma plugin took several frustrating weekends to build, not because the underlying problem was genuinely complex, but because I did not understand the basic plugin architecture model upfront and spent considerable time fighting confusion that a clearer initial understanding would have entirely avoided.
This guide walks through that foundational understanding first, before getting into actual code, specifically to help you avoid the confusion-driven delay that characterized my own first attempt.
Understanding the Two-Context Plugin Model
This is the single most important concept to understand before writing any plugin code, and it is exactly what I lacked clear understanding of initially. Figma plugins run in two distinct execution contexts that communicate with each other but cannot directly share variables or function calls — understanding this separation prevents the most common beginner confusion.
The main plugin context (sandbox): This runs your plugin’s core logic and has direct access to the Figma document — reading and modifying layers, frames, and other document content. This context, however, does not have access to standard browser APIs or the ability to display custom UI directly.
The UI context (iframe): This renders your plugin’s actual visible interface using standard HTML, CSS, and JavaScript, similar to building a simple web page. This context can use standard browser APIs and display rich UI, but does not have direct access to the Figma document itself.
Communication between contexts: These two contexts communicate via a message-passing system — the UI context sends messages to the main context (requesting some document change, for example), and the main context can send messages back to the UI (providing data to display, for example). Understanding that these contexts are genuinely separate, communicating only through this message system rather than sharing direct variable access, is the conceptual foundation that resolved most of my own early confusion once I actually understood it clearly.
Step 1: Set Up Your Development Environment
Figma plugin development uses standard web development tools and languages, making this accessible if you have any general JavaScript or TypeScript familiarity, even without prior plugin-specific experience.
Install Node.js if you do not already have it, then use Figma’s official plugin scaffolding tool (accessible through Figma’s developer documentation, which provides an up-to-date starting template) to generate a basic plugin project structure, rather than building this structure manually from scratch, which is both more error-prone and unnecessarily time-consuming compared to using the official scaffolding.
Step 2: Understand the manifest.json File
Every Figma plugin requires a manifest file defining basic plugin metadata — name, API version, and crucially, which files serve as your main context code and your UI context code respectively.
Spend genuine time understanding this file’s structure before moving further, since misconfiguration here causes confusing downstream errors that can be difficult to diagnose if you do not understand what this foundational configuration file is actually controlling within your plugin’s overall structure.
Step 3: Write Minimal Main Context Code First
Rather than attempting your full intended functionality immediately, start with the simplest possible main context code — perhaps just logging a message or selecting a single layer — confirming this minimal version actually works and successfully communicates with Figma’s API before adding your actual intended complexity.
This incremental approach, building and testing the simplest possible version before adding complexity, would have saved me considerable debugging time during my own first plugin, where I attempted to write more complete functionality immediately, making it genuinely difficult to isolate which specific part of a larger, more complex initial attempt was actually causing problems when something inevitably did not work as expected.
Step 4: Build a Minimal UI Context
Similarly, start with the simplest possible UI — perhaps just a single button — confirming this UI successfully sends a message to your main context and that your main context successfully receives it, before building out your actual intended interface complexity.
This step specifically tests the message-passing communication discussed in the foundational concept section above, and confirming this basic communication channel works correctly before adding UI complexity isolates potential problems to either the UI side or the main context side, rather than facing a more complex combined system where isolating the actual source of an issue becomes considerably more difficult.
Step 5: Incrementally Build Your Actual Intended Functionality
Once your minimal version’s communication is confirmed working, incrementally add your actual intended functionality — additional UI elements, more complex main context logic for manipulating the document — testing frequently as you add each new piece rather than building extensively before any testing.
This incremental testing approach, while perhaps feeling slower in the moment compared to writing more code before testing, genuinely produces faster overall development by catching issues immediately when they are introduced, rather than facing a larger debugging challenge when testing finally occurs after substantial accumulated, untested code changes.
Common Beginner Mistakes Beyond the Context Confusion
Assuming main context code can directly manipulate UI elements: Given the separated context model discussed above, any UI update must go through the message-passing system, not direct function calls from main context code to UI elements, which is a common early mistake stemming directly from not fully internalizing the context separation.
Not handling asynchronous operations correctly: Many Figma API operations are asynchronous, and not properly awaiting or handling these asynchronous operations causes code to proceed before an operation has actually completed, producing confusing bugs that can appear unrelated to the actual asynchronous handling issue causing them.
Insufficient testing across different document states: Code that works fine when tested against a simple, small test file sometimes breaks against more complex real-world documents with different structures than your initial testing covered, making testing across a reasonably varied range of document complexity worthwhile before considering your plugin genuinely complete.
Publishing Your First Plugin
Once your plugin works reliably, Figma’s official documentation covers the actual publishing process to the Community in detail, including specific requirements around plugin naming, description, and review process expectations, which I will not duplicate here since this process and its specific requirements are well-documented directly by Figma and worth referencing their current official guidance rather than a potentially outdated secondhand description.
A Quick Reference Development Sequence
| Step | Focus | Key Goal |
|---|---|---|
| 1 | Environment setup | Use official scaffolding tool |
| 2 | Manifest understanding | Correctly configure main and UI file references |
| 3 | Minimal main context | Confirm basic Figma API communication works |
| 4 | Minimal UI context | Confirm message-passing communication works |
| 5 | Incremental feature building | Test frequently, avoid large untested code additions |
What I Wish I Had Understood From the Start
That foundational two-context model, once I finally understood it clearly partway through my frustrating first attempt, immediately resolved the confusion that had been causing most of my actual development delay up to that point. Everything else — the specific API calls, the manifest configuration, the incremental building approach — followed naturally once that foundational architectural understanding was solidly in place.
If you take away one thing from this guide before writing any actual code, understanding that separation between main context and UI context, and that communication between them happens exclusively through message-passing rather than any direct shared access, will likely save you the kind of confusion-driven delay that characterized my own first plugin development experience.
What specific functionality are you trying to build for your first plugin? Describe your intended plugin and I can help you think through the specific implementation approach.