You want to change your app's UI without waiting for app store review. Maybe it's a button label, an onboarding flow, or an A/B test. This guide shows you how to make that happen.
We'll cover the architecture, implementation approaches, and practical steps to get started.
The Core Concept
Traditional mobile apps have UI hardcoded in the binary. Every layout, every button, every piece of text is compiled into your app. To change it, you need a new build.
Server-driven UI flips this. The UI is defined on your server and rendered dynamically on the client. Your app knows how to render components, but which components appear (and what data they show) comes from your backend.
// Server response defines the UI
{
"screen": "welcome",
"components": [
{ "type": "title", "text": "Welcome to Acme" },
{ "type": "image", "src": "hero.png" },
{ "type": "button", "text": "Get Started", "action": "navigate:home" }
]
}
The app receives this JSON and renders native SwiftUI or Jetpack Compose views. Change the JSON on your server, and the app shows the new UI — no app store review needed.
Three Approaches
There are three ways to implement this:
Build Custom
Build your own SDUI infrastructure from scratch.
Open Source
Use DivKit (Yandex), Stac (Flutter), or similar.
Platform
Use Pyramid, Judo, or Nativeblocks.
Implementation Steps (Custom or Open Source)
If you're building custom or using open source, here are the key steps:
1 Define Your Component System
Create a mapping between server component types and native views. Start simple:
text→ SwiftUIText/ ComposeTextimage→AsyncImage/AsyncImagebutton→Button/Buttonstack→VStack/HStack/Column/Row
Don't try to support everything. Start with the components you actually need for your first server-driven screen.
2 Build Your Rendering Engine
Create a function that takes a server response and returns native views:
// Pseudocode
func render(component: ServerComponent) -> View {
switch component.type {
case "text": return Text(component.text)
case "image": return AsyncImage(url: component.src)
case "button": return Button(component.text) { handle(component.action) }
case "stack": return VStack { component.children.map(render) }
}
}
3 Handle Actions
Components need to do things — navigate, call APIs, show dialogs. Define an action system:
navigate:screenId→ Push a new screenopenUrl:https://...→ Open external URLapi:endpoint→ Make an API calldismiss→ Close current screen
4 Add Your Custom Components
You'll want to use your existing design system. Register custom components that the server can reference:
// Register your design system components
componentRegistry.register("ProductCard", ProductCardView.self)
componentRegistry.register("UserAvatar", UserAvatarView.self)
componentRegistry.register("PriceTag", PriceTagView.self)
Now your server can send {"type": "ProductCard", "productId": "123"} and your app renders it.
5 Build Your Backend
Create an API endpoint that returns screen definitions. This can be:
- Static JSON — Files stored in S3/CDN, fast to update
- Dynamic API — Generated per-request, enables personalization
- GraphQL — What Airbnb uses, strongly typed
Start simple. Static JSON behind a CDN works for most use cases.
6 Add Caching
You don't want to fetch UI definitions on every screen load. Implement caching:
- In-memory cache — For current session
- Disk cache — For offline support
- Prefetching — Load next screens before user navigates
What NOT to Do
⚠️ Don't Use Hot Updates for New Features
CodePush (React Native) and similar tools let you push JavaScript updates without app store review. But Apple's guidelines restrict this to "bug fixes" — using it for new features risks app rejection.
Server-driven UI is architecturally different. You're not pushing code; you're fetching data that happens to describe UI. This is compliant with app store guidelines.
Start Small
Don't try to make your entire app server-driven overnight. Start with:
- Settings screens — Low-risk, rarely change
- Help/FAQ pages — Content-driven, easy to update
- Promotional banners — Marketing wants to control these anyway
- Onboarding flows — Frequently tested, high-impact
Once you've proven the pattern works, expand to more critical screens.
Or Skip the Build Phase
Building SDUI infrastructure takes time — typically 3-6 months for a basic system, longer for something production-ready with visual tooling.
If you want the benefits without the build time, that's why we made Pyramid.
- Visual builder — Design screens without code
- DSL option — Write screens in code if you prefer
- Drop-in SDK — Add to your existing iOS/Android app
- Your components — Register your design system
- Instant deploy — Push changes in seconds
Skip the Infrastructure Work
Get server-driven UI in your app this week, not next quarter.
Get Early Access →