React Native Runtime-Generated UI Layouts - From Figma to a Live App
10 Mar 2026Most applications still ship fixed UI layouts: screens compiled into the app binary, updated only when a new version hits the store. This approach is increasingly misaligned with modern product development. Design changes are frequent, experiments are constant, and coordination costs between designers and developers are high.
Runtime-generated UI layouts, where the UI tree is produced dynamically from JSON or design tokens derived from tools like Figma, offer a fundamentally better model. When done well, they turn layout into data, not code, and unlock a level of velocity that static UIs cannot match.
In this article I focus on what developers can do to implement this approach responsibly and effectively.
From Fixed Screens to Runtime UI Trees
The core idea
Instead of shipping precompiled layouts, the app ships a UI renderer. Layouts are described declaratively and delivered at runtime:
- JSON schemas describe view hierarchies
- Design tokens define spacing, typography, colors
- Constraints define how elements size and position themselves
At runtime, the app parses this data and builds a UI tree.
Example (simplified JSON layout):
{
"type": "Column",
"padding": "md",
"children": [
{
"type": "Text",
"token": "heading.lg",
"value": "Welcome back"
},
{
"type": "Button",
"variant": "primary",
"action": "open_profile",
"label": "View profile"
}
]
}
The app does not know this screen at compile time. It only knows how to render a Column, Text, and Button.
What developers must build
To make this viable, developers need to invest in:
- A stable, expressive UI schema
- A robust renderer
- Clear boundaries between layout and logic
This is not a shortcut, it is infrastructure.
Designers Push Changes Without App Updates
One of the most powerful outcomes of runtime layouts is that design changes no longer require app releases.
Spacing tweaks, copy updates, component rearrangements, and even new screens can be delivered remotely. This shifts the bottleneck from app stores to your backend.
Developer responsibility: enforce safety
Unrestricted runtime UI is dangerous. Developers must:
- Validate schemas strictly
- Version schemas explicitly
- Fail gracefully on unknown nodes
Example schema validation:
type Node =
| { type: "Text"; value: string; token: string }
| { type: "Button"; label: string; action: string }
| { type: "Column"; children: Node[]; padding?: string };
function validateNode(node: any): node is Node {
if (typeof node !== "object" || !node.type) return false;
switch (node.type) {
case "Text":
return typeof node.value === "string";
case "Button":
return typeof node.action === "string";
case "Column":
return Array.isArray(node.children);
default:
return false;
}
}
If validation fails, the renderer should display a fallback, not crash the app.
Feature Flags as Layout, Not Just Logic
Most teams treat feature flags as logic toggles:
if (flags.newProfile) {
showNewProfile();
} else {
showOldProfile();
}
Runtime UI flips this model. The layout itself is the flag.
Example server response:
{
"layoutVersion": "profile_v3",
"tree": {
...
}
}
Developers no longer branch UI logic. They simply render what the backend provides.
Why this is better
- No dead UI code paths
- No combinatorial explosion of flags
- Rollbacks are instant
- A/B tests become layout swaps
What developers must do differently
- Treat layout selection as data, not conditionals
- Move experiment logic server-side
- Keep client renderers deterministic
Compiling Figma-Like Constraints to Real Layout Engines
Advanced teams go further: they compile design constraints instead of hand-mapping layouts.
Design tools like Figma define relationships:
- Hug contents
- Fill container
- Fixed ratios
- Min/max constraints
These can be translated into real layout systems like Flexbox or Skia.
Constraint → Flexbox
Figma constraint:
Button:
width: hug
horizontal: fill
Compiled output:
{
"type": "Button",
"style": {
"flexGrow": 1,
"alignSelf": "stretch"
}
}
Developer responsibility: build a constraint compiler
This is not a designer problem. Developers must:
- Define a constraint DSL
- Map constraints to platform layout primitives
- Ensure deterministic output across platforms
This compiler can live:
- In CI (preprocessing Figma exports)
- On the backend
- Or even in the app (less ideal)
The key is one source of truth.
Version UI Schemas Independently from App Code
A common failure mode is tying UI schemas to app releases. This defeats the purpose.
Correct approach
- UI schemas have their own versioning
- Apps declare supported schema ranges
- Backend selects compatible schemas
Example:
{
"schema": "ui.v2",
"minAppVersion": "5.4.0",
"tree": {
...
}
}
On the client:
if (!supportedSchemas.includes(payload.schema)) {
renderFallback();
}
Why this matters
- You can ship new UI without forcing upgrades
- Old apps continue to work safely
- Deprecation becomes controlled, not rushed
Improving Developer Experience (DX)
Runtime UI systems often fail because developers hate working with them. You can fix that.
Generate types from schemas
If your UI schema is JSON, generate types automatically:
json-schema-to-typescript ui.schema.json > ui.ts
This restores autocomplete, refactoring, and safety.
Snapshot testing for layouts
Treat layouts like code:
test("profile layout v3", () => {
expect(render(layoutJson)).toMatchSnapshot();
});
This catches accidental breaking changes early.
Local preview tooling
Developers should be able to:
- Load a layout JSON locally
- Hot-reload changes
- Inspect resolved constraints
Without this, iteration speed collapses.
Performance and Caching Are Your Job
Runtime UI skeptics often complain about performance. In practice, performance issues come from bad implementations, not the model.
Developers should:
- Cache parsed layouts
- Precompute layout trees
- Avoid reflection-heavy rendering paths
Example memoization:
const cache = new Map<string, RenderNode>();
function renderLayout(id: string, json: LayoutJson) {
if (cache.has(id)) return cache.get(id)!;
const tree = buildTree(json);
cache.set(id, tree);
return tree;
}
Runtime does not mean slow. It means dynamic.
Conclusion: This Is a Structural Upgrade
Runtime-generated UI layouts are not a trend or a nice-to-have. They are a structural upgrade to how apps evolve.
Teams that succeed with this model share one trait: developers take ownership.
- They define schemas rigorously
- They build compilers, not ad-hoc mappers
- They protect stability with validation and versioning
- They treat UI as data, not magic
If you are serious about speed, experimentation, and long-term maintainability, fixed layouts are technical debt. Runtime UI is the correction, but only if developers do the hard work upfront.