Sandbox vs Serverless vs iFrame: Choosing the Right Extension Runtime
VS Code, Shopify Functions, and Figma each chose a different extension runtime. Here's how to pick the right model before you build the wrong one.
Sandbox vs Serverless vs iFrame: Choosing the Right Extension Runtime
At some point, every extensible platform hits the same fork in the road:
Where does third-party code actually run?
This decision matters more than almost any other architectural choice you will make.
It determines your security posture, your scalability limits, your developer experience, and how confidently you can evolve the platform over time. Get it right, and extensibility becomes leverage. Get it wrong, and every plugin becomes a liability.
There is no universal right answer, but there is a wrong one for your use case. Three platforms that built world-class extension ecosystems, VS Code, Shopify, and Figma, each made a different choice. Understanding why tells you more than any generic comparison chart.
The three runtime models
Most plugin systems today converge on one of these models, or a deliberate mix:
- Sandboxed in-platform runtime
- Hosted serverless runtime
- iFrame or embedded UI runtime
Each exists for a reason. None is best in isolation.
1) Sandboxed runtimes: power with containment
What it is
-
Third-party code runs inside your platform environment, but inside a strict sandbox. Typically this means:
- a restricted JavaScript runtime or separate process,
- capability-based APIs that gate access explicitly,
- memory and execution limits,
- no direct filesystem or network access unless deliberately granted.
The VS Code example
VS Code's extension model is one of the most successful in developer tooling. Extensions run in a dedicated host process, isolated from the core editor. They communicate via a message-passing API rather than direct memory access. If an extension crashes or behaves badly, VS Code surfaces the failure and stays alive.
The model is powerful because it allows rich integrations, language servers, debuggers, themes, custom tree views, while keeping the blast radius of any individual extension local. VS Code can also suspend, restart, or refuse to load extensions with known issues without touching the editor itself.
Why teams choose it
-
Sandboxing gives you:
- low latency, because code runs close to core,
- tight integration with platform data,
- strong control over permissions and resource usage.
It is the closest thing to "open plugins" without repeating the mistakes of shared-process architectures.
Trade-offs
Sandboxed runtimes are harder to build correctly. They are restrictive by necessity. Debugging can be painful unless you invest in tooling. Developers who want full system access will feel the constraints.
Best for
- core workflow extensions that must be synchronous,
- logic that needs tight access to platform internals,
- high-trust environments where isolation is non-negotiable.
Red flag
If sandboxed code can still block the main thread, bypass permission checks, or access shared globals, you do not actually have a sandbox. You have the appearance of one.
2) Serverless runtimes: isolation by default
What it is
-
Extensions run as functions on managed infrastructure, typically outside the core application lifecycle. Think:
- serverless functions invoked on specific events,
- scoped credentials passed at runtime,
- stateless execution with explicit timeouts.
The platform invokes extension code but does not host it in-process.
The Shopify Functions example
Shopify Functions is one of the clearest modern examples of serverless extensibility done well. Merchants and developers can customise checkout logic, discounts, payment methods, and cart rules using functions that compile to WebAssembly and execute at the edge.
The design decision is deliberate. Shopify owns checkout reliability at scale. By requiring extensions to compile to a sandboxed WASM bytecode with explicit execution time limits, they preserve predictability for the platform while unlocking significant power for partners. An extension cannot make external HTTP calls during execution, cannot persist state arbitrarily, and cannot delay the checkout flow beyond a fixed budget.
The constraint is also the feature. Developers know exactly what the runtime allows. Shopify knows exactly what the runtime will do under load.
Why teams choose it
-
Serverless offers:
- strong isolation with natural failure boundaries,
- scalability without per-extension resource management,
- clear separation between platform logic and extension logic.
If a function crashes, it crashes alone.
Trade-offs
Serverless runtimes introduce higher latency, async-first execution models, and require careful thinking about retries and idempotency. They are less suited to real-time UI interactions or operations that need immediate feedback.
Best for
- automations and workflows,
- background processing and integrations,
- untrusted or lower-trust code that needs strict resource enforcement.
Red flag
If serverless extensions can run indefinitely, make unrestricted network calls, or escalate permissions dynamically, you have recreated the same risks at a distance. The serverless label does not automatically mean safe.
3) iFrame runtimes: surface-level extensibility
What it is
-
Extensions render UI inside defined host surfaces using embedded frames. They:
- run in their own browser context,
- communicate with the host via structured message passing,
- have no direct access to platform internals.
The Figma example
Figma built its plugin and widget system on iFrames, and it was a considered choice. Plugins run in a sandboxed iFrame that can communicate with the main Figma canvas via a defined postMessage API. The canvas itself runs in a separate context where plugins have access only to an explicit API surface.
This model meant Figma could open the ecosystem to external developers quickly, without exposing the rendering engine to arbitrary code. Plugin authors write JavaScript that interacts with the Figma API rather than the underlying C++ renderer. The boundary is clear, enforced by the browser's own origin isolation.
The tradeoff is real: plugins feel slightly disconnected from the native canvas. Cross-origin messaging adds overhead. Complex UI interactions are harder to build. But for Figma's goals at the time of launch, the security and simplicity of iFrame isolation outweighed those costs.
Why teams choose it
-
This model is:
- relatively simple to implement,
- safe by default because isolation is enforced at the browser level,
- familiar to web developers.
It is excellent for extending user-facing surfaces without extending trust too far.
Trade-offs
iFrame-based extensions can feel disconnected from native UI. They require well-designed messaging APIs to be usable. Deep system access is not available by design.
Best for
- dashboards and panels,
- visual tools and contextual UI enhancements,
- early-stage ecosystems where speed to launch matters more than deep integration.
Red flag
If embedded UI extensions can inject scripts globally, manipulate DOM outside their surface, or access user data implicitly, isolation is cosmetic. The iFrame boundary must be meaningful, not decorative.
The real insight: most mature platforms layer all three
One of the most common mistakes teams make is forcing everything into a single extension model.
-
Strong platforms compose their runtimes:
- UI extensions use embedded surfaces,
- automations and workflows use serverless functions,
- core logic uses sandboxed runtimes.
Figma has since expanded beyond pure iFrame plugins. Shopify supports both serverless Functions and traditional REST-based app extensions depending on the use case. VS Code allows extensions to spin up language servers that communicate via the Language Server Protocol.
Each runtime has a clear purpose, a defined risk profile, and explicit constraints. The composition scales far better than a one-size-fits-all plugin system.
Choosing the right runtime: a decision matrix
Ask these questions for each extension surface you are designing:
-
How sensitive is the data?
- High: sandboxed or serverless with strict scopes.
- Medium: serverless.
- Low: embedded UI.
-
How much latency is acceptable?
- Must be instant: sandboxed or in-platform.
- Async is acceptable: serverless.
-
What is the blast radius of a failure?
- Must be zero: serverless or iFrame.
- Acceptable but limited: sandboxed process.
-
Who are the builders?
- Power developers building deep integrations: sandboxed and serverless.
- Broad external ecosystem: iFrame and serverless.
- Internal teams with controlled environments: hybrid.
Runtime decisions are governance decisions
Your runtime model determines what permissions exist, how reviews work, what enforcement is possible, and how confident customers feel when they install an extension.
Weak runtimes force heavy governance. Strong runtimes make governance lighter. This is why infrastructure decisions are governance decisions, not just engineering ones.
Migration matters: do not paint yourself into a corner
Many platforms start with iFrame-based extensions, then add serverless workflows, and later introduce sandboxed runtimes for power use cases. That is a healthy evolution.
What is dangerous is launching unconstrained in-process plugins and then trying to retrofit isolation later. Containment is hardest to add after the fact. The WordPress ecosystem is still paying the price of that decision.
Forge's POV: runtime design is platform destiny
Choosing an extension runtime is not a technical detail. It is a statement about how much you trust third-party code, how much risk you are willing to carry, and how far your ecosystem can scale.
The best platforms do not ask: "Which runtime is easiest to ship?" They ask: "Which runtime lets us sleep at 10x scale?"
VS Code, Shopify, and Figma answered that question before they had 1,000 extensions to worry about. That is why their ecosystems compound rather than accumulate debt.