Skip to content

FIGMA 2026-04-21 Tier 3-equivalent

Read original ↗

How We Rebuilt the Foundations of Component Instances

Summary

Year-long Figma client-architecture rewrite (2025, 15+ contributors) replacing Instance Updater — the 2016-era self-contained runtime that resolved component-instance properties, children, and layout — with Materializer, a generic framework for maintaining derived subtrees of the document tree whose structure and properties are computed from other sources of truth via blueprints. Component instances become one client of Materializer; rich-text-from-CMS nodes are another; slots (open beta) composes on top rather than introducing bespoke reactivity. Core design choices: separate "what to compute" (feature-owned blueprints) from "how to keep it fresh" (framework-owned invalidation); push-based invalidation with automatic dependency tracking (reject pull-based for Figma's cross-tree-reference reality); dependencies recorded implicitly as nodes read data during materialization (no manual declare_depends_on API). A second thrust unified Figma's runtime systems (layout / variables / instance resolution / constraints) under a shared orchestration layer running updates in predictable order, which surfaced and let the team eliminate "back-dirties" — hidden feedback loops where later systems invalidate work done by earlier ones — moving the runtime toward unidirectional flow. Rollout gated by side-by-side runtime validation against hundreds of thousands of production files, comparing both data models and rendered output across correctness and performance before flipping live. Reported result: one of the most expensive edit classes — variable-mode changes in large files — improved 40–50%, "representative of broader gains" across common instance edits. Perhaps biggest return: developer velocity — rich text shipped as the first net-new feature on Materializer; slots composes on top rather than reimplementing reactivity.

Key takeaways

  1. Separation of concerns: a generic reactive framework vs feature-owned blueprints. Materializer doesn't know about component instances, rich text, or slots. Each feature supplies a blueprint describing how a subtree should be derived from its source of truth (for instances: what properties to inherit from the main component, which overrides apply, which children should exist; for rich text: how <h1>/<p>/<img> blocks map to text + image nodes). Materializer tracks dependencies, invalidates stale data, and re-materializes only what changed. This is the explicit anti-goal of the old Instance Updater, which had accreted its own auto-layout + variable-evaluation logic.

  2. Push-based invalidation chosen over pull-based for cross-tree workloads. Pull-based ("check staleness on read," React-style) requires no explicit dep graph but "breaks down in a system like Figma's" — cross-tree references and deeply nested dependencies force reconstructing large portions of the dependency chain on every read. Push-based ("explicit dep graph, mark dependents dirty on source change, recompute later") trades bookkeeping for precise control "over what needs to update, and just as importantly, what doesn't." The choice is workload-shape-driven: cross-tree deps + deep nesting → push.

  3. Automatic dependency tracking is the refinement that makes push-based tractable. "As nodes read data during materialization, Materializer records those relationships implicitly. Developers don't declare dependencies by hand; the system learns them as it runs." Eliminates the category of bugs where a hand-declared dep graph drifts from the code's actual reads. Structural cousin to MobX / SolidJS proxied reactive reads, applied to a document tree rather than application state.

  4. Granular invalidation is the direct perf payoff. The old system "updated entire instances whenever anything changed" → recursive cascades of work that locked the editor. Materializer updates "only parts of the tree that actually changed." The canonical measured win: variable-mode changes in large files improved 40–50% — "one of the most expensive cases." Old-system failure modes named: in some files changing a single variable mode triggered cascading updates + layout recalcs across thousands of nodes; different systems would "fight" over the same subtree, repeatedly invalidating each other; instance-swap / property-change could take seconds and lock the editor.

  5. A second unification: runtime orchestration under a shared layer to eliminate back-dirties. Beyond Materializer itself, Figma unified its layout engines, variable resolvers, and constraint enforcers under a common framework that runs updates in a predictable order. That predictability surfaced previously-hidden feedback loops where later systems would invalidate earlier ones and force re-runs — Figma calls these back-dirties. Making them explicit is what enabled eliminating many of them and moving the client closer to unidirectional flow: source-of-truth changes trigger all side effects across systems in a predictable direction.

  6. Separation of concerns is also the ownership payoff. "Layout logic lives in layout systems. Variable logic lives in variable systems. Instance resolution no longer tries to coordinate everything itself. Product teams can now own their features vertically, building on shared infrastructure instead of re-solving the same orchestration problems." The framing: incremental extensions of a bespoke runtime eventually exhaust their cross-team coordination budget; a proper substrate is the escape.

  7. Risk discipline: side-by-side runtime validation on real files before flipping live. Porting instance resolution from self-contained into shared runtime introduced "subtle differences in execution order and behavior" surfaced only in edge cases not covered by unit tests. Rollout gated on: running old and new runtimes in parallel across hundreds of thousands of real production files for months, comparing both data models and rendered output, collecting both correctness and performance metrics side-by-side. Supporting automatic reactivity required additional dependency caches — improved interactive perf, carried potential cost on file load + memory. Only when old and new matched on both axes did gradual production rollout begin. Stronger form of the patterns/shadow-validation-dependency-graph discipline already seen in QueryGraph.

  8. Developer velocity as the biggest long-term return. "Without this framework, adding new kinds of dynamic content would have meant reimplementing reactivity and invalidation from scratch." Concrete payoffs: rich text nodes (CMS-backed, open beta) as the first net-new feature built on Materializer; slots (open beta) "built by composing on top of the new architecture rather than introducing another bespoke system." Other teams "across Figma's products are already building on the Materializer framework to ship new kinds of dynamic content."

Architectural numbers / shape

  • Team / timeline: year-long project, 17 named contributors.
  • Old system (Instance Updater): introduced 2016; self-contained runtime for instance-property resolution, structure management, sync with main components. Other systems (layout engines, variable resolvers) delegated to it when encountering an instance.
  • Triggering feature accretion (decade 2019–2024): auto layout (2019), variants (2020), component properties (2022), variables (2023). Each extension added bespoke logic.
  • Failure-mode worst cases:
  • Single variable-mode change → cascade across thousands of nodes.
  • Different systems "fight" over the same subtree (mutual invalidation).
  • Instance swaps / property changes: seconds, editor-locking.
  • Invalidation axis: old = whole-instance; new = property-granular (the same shift Figma's parameter-runtime unification delivered on a different axis).
  • Reactivity model: push-based dep graph + automatic implicit dependency recording during materialization.
  • Canonical measured improvement: variable-mode changes in large files 40–50% better.
  • Validation scale: hundreds of thousands of real files, months of parallel execution, data-model + rendered-output + perf comparison pre-rollout.
  • Known cost axis of the new design: "additional dependency caches" — named potential cost surfaces are file load time and memory usage, compensated by interactive-perf gain.
  • Already-built second-client feature: rich text nodes — text + image subtree derived from rich-text blocks (<h1>, <p>, <img>) that are themselves synced with a CMS source of truth.
  • Composition example: slots (open beta), shipped in April 2026, built "by composing on top of" Materializer.

Caveats

  • No absolute latency numbers for individual Materializer stages (record deps, mark dirty, re-materialize).
  • No memory-cost numbers for the implicit dependency cache at production-file scale.
  • Push-vs-pull comparison is framed as workload-reasoned, not numerically. React is named as the pull-based reference; no explicit Figma benchmark of the two inside the same substrate.
  • "Back-dirty" is a named phenomenon; the post does not enumerate which systems had them, how many were eliminated, or any of the remaining ones.
  • Automatic dependency tracking is described as "records those relationships implicitly" — no detail on the mechanism (read-hook vs proxy vs code-gen), cost, or false-positive behavior when a materialization reads more state than it actually depends on.
  • Migration strategy for existing files is not described beyond "ran the old and new runtimes in parallel…comparing both data models and rendered output."

Relationship to prior Figma wiki pages

This post sits alongside the Parameter Runtime retrospective (also 2026-04-21) and the QueryGraph dynamic-loading post (2024-05-22). All three are reactive-graph designs over Figma's object-tree document model, but index different edges:

  • QueryGraph — read+write dependencies between document nodes (FK edges like instance→component plus implicit layout/constraint edges). Powers per-session subscription + edit fan-out.
  • Parameter Runtimeparameter-to-bound-property edges. Powers reactive re-resolution on parameter-value changes.
  • Materializer — source-of-truth → derived-subtree edges, with automatic dependency tracking. Powers derived-subtree maintenance for component instances, rich text, slots, and future dynamic content.

All three share the "bidirectional in-memory index over document state, maintained by the client runtime, invalidated property-granularly" shape; they differ in what they index. The 2026 runtime-orchestration-layer unification described in this post is the substrate through which all three (plus layout engines, variable resolvers, constraint enforcers) now interoperate in a predictable update order — the primitive that surfaced back-dirties and moved the client toward unidirectional flow.

Material connection to patterns/shadow-validation-dependency-graph and patterns/unified-typespace-consolidation: side-by-side runtime validation is a stronger form of shadow validation (old and new run together; shadow had one authoritative); runtime orchestration unification is the runtime-side complement to the parameter-system data-model unification, both instances of Figma collapsing accumulated bespoke subsystems onto shared platform infrastructure.

Original article

Last updated · 200 distilled / 1,178 read