On Server-Driven UIs
Sep 2025
When reading engineering blogs I always thought to myself: “Will I ever write a blog?” or “Why do people like writing blogs so much?”. Now I finally understand it: sometimes you just need to vent.
I think I have many learnings to share after working for several months building the backend of a project that featured a server-driven UI in core parts of the UX.
There are some blog posts on the internet about this rendering technique (Airbnb has one that we will analyze later) and most share something in common: they were written by the people that built the system, not the ones that took it down.
So let’s start from the beginning.
What is even a Server-Driven UI?
When defining user interfaces, you need to tell the rendering engine the size of the text, the width of an image or the spacing and arrangement of buttons.
For example, if you are coding in SwiftUI (the iOS official framework) you would write something like this:
VStack {
Text("Hello, World!")
.font(.title)
.padding()
Image("example-image")
.resizable()
.frame(width: 100, height: 100)
Button(action: {
print("Button tapped")
}) {
Text("Tap me")
}
}
This code will be compiled and bundled in the app binary that is downloaded by the user when installing the app from the App Store.
This is how most apps work, and it is the most straightforward way to build user interfaces. We can also call it a client-driven UI, as the client (the app) is the one that defines how the UI looks like.
In a more realistic example, this is what usually happens:
- The backend sends structured data with just the content of what needs to be displayed. *Example: a json payload with a title, a date and an image URL.
- The app parses the data.*
- The app decides how to render the UI based on the received data. Example: if the date is more than 5 years ago, show it in red.
So far so good.
However, some day, someone will point to two of the main issues of client-driven UIs:
- It takes weeks to update the UI. Since the UI definition is in the binary,
- First you need to build a new release.
- Then get it approved by Google/Apple (usually takes days).
- And finally wait for the customers to update (some will take months to update).
- If you have several platforms (iOS, Android, Web, Desktop, LG webOS, etc.) you need to implement the same UI logic in all of them.
- Wouldn’t it be nice to have a single codebase instead of five?
- Wouldn’t it be nice to have consistency across platforms much more easily?
And this is the moment when someone will say:
Why don’t we just send the UI definition from the backend?
Choosing a Server-Driven UI
Now we have very strong motivations to use some kind of server-driven UI: it will make us more agile, consistent and it will save us time.
The first step is to look into existing implementations and user stories. After looking in the internet, you will find the famous Airbnb article about how they built their server-driven UI.
You will also find some open source libraries like DivKit where the UI is defined in json and rendered in iOS & Android using native components.
Also, you might realize that HTML is a server-driven UI technology: the backend sends HTML to the browser, and the browser renders it. Both Data + UI are sent at the same time and defined in the backend. In the last years, this field has evolved a lot with frameworks like React that render many things in the client side (browser).
In the project I was involved, HTML was discarded because you need to create WebViews in the app, which have a lot of bad reputation amont native developers since they don’t allow you to fully use the native capabilities of the platform and archieve a high quality UX.
The 3rd party libraries were also discarded because they were not mature enough and we wanted to have full control of the quality and look & feel of the UI.
So we decided to set out to build our own server-driven UI system.
Creating a Server-Driven UI
The schema
You will have to create your own UI schema that is platform agnostic. This is not a trivial task because you have to take into account the capabilities of all the platforms you want to support.
After a lot of iterations, you might end up with something like this:
{
"type": "container",
"direction": "vertical",
"children": [
{
"type": "text",
"text": "Hello, World!",
"font": {
"size": 24,
"weight": "bold"
},
"padding": 16
},
{
"type": "image",
"url": "https://example.com/image.png",
"width": 100,
"height": 100
},
{
"type": "button",
"text": "Tap me",
"action": {
"type": "navigate",
"url": "app://next-screen"
}
}
]
}
This might look simple, but if you want to have a customizable UI where you can define styles, colors, fonts, spacings, etc. you will end up with a very complex schema.
This is probably the hardest part*: you need to find the right balance between flexibility and simplicity. If your schema is too simple, you won’t be able to create complex UIs. If your schema is too complex, it will be hard to use and maintain.
A nice trick is to tie the schema to your design system. For example, instead of allowing any font size, you can define a set of predefined sizes (small, medium, large) that map to your design system. This will make maintenace easier since you will reduce the combinations and amount of properties.
The rendering engine
Then, you will have to implement the rendering engine in all the platforms you want to support. This is also not a trivial task because you have to parse the schema and render a component one by one.
In some platforms you will find tricky bugs and limitations during the rendering process. Indeed, this will be a big challenge: ensuring consistent and reliable rendering across different platforms.
The rendering engine on each platform will be an important piece that will require its own extensive testing and maintenance. Don’t underestimate the effort needed here.
Versioning
You will have to create a versioning system for your schema. Why? Because older clients could be around for months (and years) and you will want to ensure that if you change the color representation old clients are not going to break.
Indeed, you will find that each time you change the schema in the backend you will have to ensure it renders as expected not only in the latest app version, but also in older versions.
Other problems
I will keep expanding this section as I collect more stories from the industry.
What nobody tells you about
In the last section we have seem some significant pieces you will have to build.
If you are considering creating any kind of server-driven UI, I’m going to write here some of the problems that you need to know before starting:
-
Cost: everyone always underestimates the cost of building and maintaining a server-driven UI system. This is what you will have to maintain:
- A schema that can accomodate the idiosyncrasies of all the platforms you want to support.
- A new backend service that easily allows to create the UI schema in a readable and easy way.
- A rendering engine per platform with an extremely high quality bar in terms of testing.
- A versioning system in the backend to ensure backward compatibility.
- A fast iteration system so that developers can easily see if the UI changes they did are rendered as expected in all the platforms.
- Extensive, high-quality documentation of the schema so that new developers (and AI) knows how to use it.
-
Reduction of agility: apart from all the overhead months for developing a SDUI and training your developers, making a UI change in the backend will be slower than just doing it in the frontend.
- Why? Because each time you make a change you will have to ensure it renders and behaves correctly in all the platforms. If you have 3 platforms (iOS, Android, Web), you will be working with 4 codebases.
-
Loss of quality: native experiences are about the small details in the interactions.
- If you want to make that button bounce in a way when tapped, add that haptic feedback, make an animation for this case, etc. with a SDUI the complexity will be higher than just doing it natively.
-
Exponential complexity: if you want to make the SDUI more capable so that developers have more freedom to harness the power of native, the schema and engines will become too complex to maintain.
-
High risk: you might nail the balance of flexibility and make a marvelous SDUI with a good developer experience and overall makes things better. It’s also possible that you don’t, like Airbnb did. The Airbnb article that I linked earlier is outdated: one year later they dropped support of everything except ordering of sections, which is far from the original vision and far from a SDUI.
Summary
A server-driven UI will save us time!
No, it probably won’t. It will slow your time to market, introduce a lot of development processes, increase the mental workload of devs, and a lot of surprises will come up because you are doing something that is not standard across the industry.
A server-driven UI will allow us to have a single codebase!
True, but just for the UI. In reality, you will end up with N + 1 codebases because you need to maintain all the rendering engines + the backend where the UI is defined.
A server-driven UI will allow us to skip the app store review process!
True, once everything is working (after 6+ months of development), you will be able to change the UI fast if and only if you don’t need to expand the schema because marketing team told you that pink drop shadows convert 10% better and the schema does not support custom shadow colors.
Lessons learned
I choose a lazy person to do a hard job. Because a lazy person will find an easy way to do it.
— Bill Gates
Before implementing a server-driven UI, you should ask yourself:
- Why do I really need a server-driven UI? Is there another way of solving the root problem?
- Do the advantages outweight the costs by a very wide margin? Is there a strong business case where the investment pays off?
- Do I fully understand all the costs? Have I compared side by side how much people will I need to maintain the SDUI vs a client-driven UI?
And the most important question I think every engineer should ask themselves always:
— Is there a simpler way to solve this problem?
Anyway, I think that server-driven UIs have their place in some scenarios. For example, RevenueCat’s paywalls are server-driven, and I think it’s well worth it because it’s a killer feature that is helping this company outcompete Stripe.
Finally, please, if you have successfully implemented a SDUI and maintained it for more than 2 years 😉, send me an email to hi (a) raporpe.me
! I’d love to hear success stories.
See you in the next post! 👋🏼