Server Driven UI, Personalization, Runtime Bundling
I’ve recently been investigating the concept of server-driven UI (SDUI) and i believe it has huge potential to power a new breed of highly dynamic and personalized applications across multiple platforms.
In a traditional world, data is driven by the server, but the UI is driven by each client. With SDUI, both the data and UI are driven by the server
This was the first concept that struck me, traditionally each client is responsible for picking up the data from the server and turning it into a UI representation. We usually take this model for granted as a default, but there’s often client-specific logic spread across multiple platforms that need to maintain parity with each other.
The concept of SDUI is mostly mentioned in the mobile native-app development space. These platforms require shipping versions of your app to a store that usually has a lengthy review process before a new version gets published. With SDUI these clients are more autonomous to release since they can push changes to a server, without changing the app binary and therefore without going through the app store process.
Server-driven UI continues to be a hot topic of discussion in mobile circles because it offers the potential for developers to take advantage of faster change cycles without falling foul of an app store's policies around revalidation of the mobile app itself. Server-driven UI separates the rendering into a generic container in the mobile app while the structure and data for each view is provided by the server. This means that changes that once required a round trip to an app store can now be accomplished via simple changes to the responses the server sends… https://www.thoughtworks.com/radar/techniques/server-driven-ui
However in a world of increasing personalization, we may not even know exactly what components will be on the page or in each order they will appear, we may be making some of these decisions in runtime, and in this scenario, the complexity, the duplication between platforms and feature parity becomes more significant and harder to achieve.
This mixed with a large number of A/B tests across these components creates a pretty complex dependency graph and a big issue to solve. This is where i think this concept can be interesting: highly dynamic UIs.
Airbnb’s specific SDUI implementation enables our backend to control the data and how that data is displayed across all clients at the same time. Everything from the screen’s layout, how sections are arranged in that layout, the data displayed in each section, and even the actions taken when users interact with sections is controlled by a single backend response across our web, iOS, and Android apps.
SDUI
Let’s start with the concept of having a UI representation in data, what if we could get from the server instructions on how to render the UI?
{
"screens": [
{
"id": "ROOT",
"layout": {
"type": "SingleColumnLayout",
"main": {
"type": "MultipleSectionsPlacement",
"sectionDetails": [
{
"id": "hero_section",
"sectionComponentType": "HERO",
"section": {
"type": "HeroSection",
"images": [
"api.x.com/..."
]
}
},
{
"id": "title_section",
"sectionComponentType": "TITLE",
"section": {
"type": "TitleSection",
"title": "Beach Cottage",
}
}
]
},
"footer": {
"type": "SingleSectionPlacement",
"sectionDetail": [
{
"id": "book_bar_footer",
"sectionComponentType": "BOOK_BAR_FOOTER",
"section": {
"type": "ButtonSection",
"title": "$450/night",
"button": {
"text": "Check Availability"
}
}
}
]
}
}
}
]
}
//Adapted from: https://www.infoq.com/news/2021/07/airbnb-server-driven-ui
This would give us a consistent UI representation across platforms with logic centralized on the server about which components to show in which position, with everything already translated and localized. Each platform would only need to build the appropriate components according to the contract defined.
This requires a tight alignment with a design system language providing a strong collection of components that allows applications to map component types to UI components to be rendered.
Clients can then have a collection of types mapped to which UI component they should use and render it with the correct data.
const { data } = useQuery(RootScreen);
// Map type to UI component
const TYPE_COMPONENTS = {
ProductList: ListComponent,
ProductCarousel: CarouselComponent,
ProductError: ErrorComponent
};
const component = TYPE_COMPONENTS[data.rootScreen.__typename];
render(component, data);
This highly dynamic capability comes with some issues, especially for web apps. When it comes to bundling if we don't know which components are present on a page, so how can we create an optimized bundle?
This shouldn't be as big of a concern in mobile apps since they probably already ship a full binary to the app store with all components. However, on the web, there's no ahead-of-time install step, and loading all components upfront would likely have a major impact on performance.
Most approaches I’ve seen so far either load all components upfront or load all components async on the client, creating the potential for resource contention with a large number of requests, neither seems to be scalable for a large-scale production rollout or for more than some contained specific use-cases.
Runtime Bundling
The concept of runtime bundling is something I’ve been thinking about for a while that seems to me wildly unexplored. In a world of very fast bundlers such as esbuild, turbopack or even more recently rspack is it feasible to create a bundle for a dynamic page in runtime?
This benchmark approximates a large JavaScript codebase by duplicating the three.js library 10 times and building a single bundle from scratch, without any caches. The benchmark can be run with
make bench-three
in the esbuild repo.esbuild:0.37s
This can be made faster by creating cache mechanisms and by doing expensive work ahead of time such as minification of code. This would allow the creation of highly dynamic and optimized pages on the fly but seems to be a largely unexplored path. I’ve only seen one talk from netflix about doing this in production at a large-scale company.
This concept when applied with SDUI seems worthy of further exploration.
Conclusion
In a world of highly dynamic UIs, this architecture can play a role if well-handled and carefully thought-out. Additionally, it can be applied only to more dynamic areas of your application. This is a pattern that you can adapt to your own needs, it doesn't need to take over your entire UI and make everything fully dynamic, at least not all at once.
Implementing this architecture can also have interesting properties in scaling development as it shifts the focus of frontend teams to developing individual components that are assembled at runtime. This can create an interesting case for independent deployments and promote a fast pace of change.
Resources
https://www.apollographql.com/docs/technotes/TN0032-sdui-and-graphql/?utm_source=pocket_reader
https://medium.com/airbnb-engineering/a-deep-dive-into-airbnbs-server-driven-ui-system-842244c5f5