Most developers who upload mobile apps to the App Store or Play Market have likely encountered unexpected bugs in new releases. To address this issue, a new build with the fix must be created and checked again by the platform where they are uploading. They also have to wait for the approval process again for the new version to be released. Unfortunately, this entire process takes a considerable amount of time, but there is an alternative way.
The solution is to use an approach called Backend Driven UI. During my work with mobile applications in various fields, the Backend Driven UI approach has helped me effectively optimize the development process. I will introduce you to this approach in more detail, along with its advantages and usage features.
What is Backend Driven User Interface (BDUI)
Let’s break down what Backend Driven UI is. This is a development approach that involves transferring the business logic and functionality of components to the server side, while the client side, in turn, is built according to instructions from the server.
How Backend Driven UI Streamlines Dynamic Updates
Both the App Store and Play Market have virtually identical rules and recommendations regarding changes that can be made dynamically without resubmitting the application. Here is a real example of when changes can be made dynamically: I have a page displaying a list of products with general information about them, but later I need to add, for example, the product’s serial number. Such a change does not require resubmitting the application and can be implemented using the Backend Driven UI approach. You can find information about App Store recommendations at this link: App Review Guidelines.
Advantages of Using Backend Driven UI
The main advantages of this concept are as follows:
1.Simplification of User Interface Updates. This approach allows us to adapt and update the user interface without needing to update the mobile application on digital distribution platforms. This enables very quick responses to user requests and needs.
2.Ease of Implementing New Features. Any changes to existing features or the implementation of new features will be deployed faster, which in turn speeds up the testing process.
3.Scalability and Flexibility. The UI largely depends on the server, which controls the content and appearance of the interface through instructions. This allows for managing different versions of the application for specific user segments. It is also useful for A/B testing.
4.Content Unification. The server dynamically manages and adapts the content and appearance for each individual user. Combined with data on user behavior and preferences, this allows for creating a highly personalized interface.
5.Lower Maintenance Cost. With reduced time for development, deployment, testing, and release on the App Store and Play Market, the costs are proportionally decreased. Additionally, with easier version control, the likelihood of errors occurring during user updates is reduced.
Disadvantages of Using Backend Driven UI
This approach also has several disadvantages. These include:
1.Increased Server Load. Since the business logic and instructions for building the user interface are also on the server, the load on the server increases accordingly. To ensure stable operation, server resources will need to be increased.
2.Greater Dependence on Internet Connection. Since the business logic and instructions for building the user interface are on the application’s server, performance issues may arise with poor internet connections.
3.Complicated Server Architecture. Using the BDUI concept complicates the architecture, which to some extent can create difficulties in applying well-known architectural approaches and lead to code bloat.
4.Delays in Content Updates. Based on the previous points, it can be understood that if the server is heavily loaded, the delay time for updating content on the application’s user interface will increase. This is because any changes in the UI require instructions from the server.
Real Applications That Use Backend Driven UI
I think everyone knows products like Facebook, Airbnb, Amazon, Netflix, Spotify, and Uber. At first glance, they all seem very different, with completely distinct functions and needs, and have little in common. However, they all, to varying degrees, use the BDUI concept.
For instance, in the Uber app, it involves displaying dynamic trip information. The content and appearance of the dynamic data that the user sees are controlled by the server. Spotify, as well, uses BDUI to display playlists, recommendations, and other musical content. This allows the app to use server data to change the content and display the interface in real time.
Airbnb has utilized a UI system called Ghost Platform. This system accelerates development iterations and feature launches across all platforms by providing frameworks in the native languages of the client’s platform.
Comparison with Server Side Rendering (SSR)
When I first heard about Backend Driven UI, I was convinced that it was the same as Server Side Rendering. These approaches share one concept – transferring business logic to the server side to simplify the client side as much as possible.
However, there is also a very important distinction between them. In BDUI, the server controls the content and appearance of the interface through instructions that the client side uses when building the interface. On the other hand, SSR independently controls the process of forming the interface and sends the client side a ready-made HTML template.
Below is a comparative table of the main criteria between these approaches:
Сriterion | Server Side Rendering (SSR) | Backend Driven UI (SDUI) |
Principle | Server defines the layout and structure by generating instructions | Layout rendering happens on the server side before sending to the client |
Rendering | Rendering happens on the client side based on server instructions | HTML is rendered on the server |
Performance | Faster initial rendering speed, but subsequent performance may degrade. | Faster for dynamic UI changes. |
SEO | All content is immediately available for search engine algorithms, which is good for SEO. | Less effective due to dynamic rendering. |
Caching | Easily caches fully rendered pages. | Caching is complicated due to the dynamic nature of the interface. |
Dynamic | Limited | Very flexible |
Support | Simple to support static content. | More complex to support due to the need for detailed synchronization between client and server parts. |
Example:
Now that we’ve familiarized ourselves with the principle of BDUI, let’s move on to practice. Let’s assume we already have a released mobile application that displays a list of items for sale — in my case, shoes.
Below, I will provide partial code examples, and you can view the entire code on my GitHub repository.
To start, I created three hooks:
1.useFetchData – for fetching instructions from the server side
export const useFetchData = () => { const [instractions, setInstractions] = useState<IItemInstractions[]>([]); useEffect(() => { axios .get("http://localhost:3000/") .then((response: { data: IItemInstractions[] }) => { setInstractions(response.data); }); }, []); return { instractions }; };
2.useRenderComponent – a universal hook for rendering pre-created components within the project root.
export const useRenderComponent = () => { const render = useCallback(({ name, props, children }: IInstractions) => { const Component = lazy(() => import(`../components/${name}`)); if (children) { return <Component {...props}>{children}</Component>; } return <Component {...props} />; }, []); return render; };
3.useRenderItemsList – for rendering an item directly from a list.
export const useRenderItemsList = (instractions: IItemInstractions[]) => { const [components, setComponents] = useState<ReactNode[]>([]); const render = useRenderComponent(); const renderListItem = useCallback( (itemInstructions: IItemInstractions) => { const images = itemInstructions.images.map((image) => render(image)); const texts = itemInstructions.texts.map((text) => render(text)); const actions = itemInstructions.actions.map((action) => render(action)); return ( <StyledItem> <StyledRow> {images} <StyledTextContainer>{texts}</StyledTextContainer> </StyledRow> <StyledActions>{actions}</StyledActions> </StyledItem> ); }, [render] ); useEffect(() => { if (instractions) { setComponents( instractions.map((instraction) => renderListItem(instraction)) ); } }, [instractions, renderListItem]); return { components }; };
Finally, i also created three components: Button, Image, TypographyText. Here’s an example of code just for the button:
const StyledButton = styled(AntdButton)<IButtonProps>` &.ant-btn { display: flex; align-items: center; justify-content: center; height: ${(props) => props.$height || "100%"}; color: ${(props) => props.$color || "white"}; width: ${(props) => props.$width || "100%"}; min-width: ${(props) => props.$minWidth}; padding: ${(props) => props.$padding || "8px 0"}; margin: ${(props) => props.$margin}; font-size: ${(props) => props.$fontSize || "16px"}; line-height: ${(props) => props.$lineHeight || "20px"}; font-weight: ${(props) => props.$fontWeight || 400}; background: ${(props) => props.$background || "black"}; border: ${(props) => props.$border || "1px solid"}; border-color: ${(props) => props.$borderColor || "transparent"}; cursor: pointer; } &:focus { border-color: transparent; } &.ant-btn[disabled] { background: #efefef; color: #606060; cursor: not-allowed; } `; const Button = ({ children, ...props }: IButtonProps) => { return <StyledButton {...props}>{children}</StyledButton>; }; export default memo(Button);
For completeness, here’s how my App.ts looks:
function App() { const { instractions } = useFetchData(); const { components } = useRenderItemsList(instractions); return ( <Container> <Suspense fallback={<div>Loading...</div>}>{components}</Suspense> </Container> ); } export default App;
So, how it works is, in App.ts I call the hook to fetch instructions from the server, then the hook renders predefined component templates based on those instructions. It looks like this:
The server-side requires a service that generates instructions as well as component templates used for generation.
etItems(): IItemListInstraction[] { // fetch data const products = data.items; // generate instructions for products const instructions = products.map((prodcut) => ProductComponent(prodcut)); return instructions; }
Here’s an example of how the generated component might look like:
export const ProductComponent = ( prodcut: IProduct, isDisabled?: boolean, ): IItemListInstraction => { return { images: [ { name: 'Image', props: { src: prodcut.img, width: '145px', height: '72px', }, }, ], texts: [ { name: 'TypographyText', props: {}, children: prodcut.name, }, { name: 'TypographyText', props: {}, children: prodcut.size, }, ], actions: [ { name: 'Button', props: { $width: '150px', disabled: isDisabled, }, children: 'Buy now', }, ], }; };
So, we retrieve the data, in my case it’s hardcoded, and then for each item, we create a component that holds information about images, text, and actions that can be taken with each item.
Now, a requirement has come up and for certain items, we need to add an ‘Add to Cart’ button. With the traditional development approach, we would have to make changes on the client side, go through the release process again, and only then would users see the changes. But since we are using BDUI, we can do this as follows:
const instructions = products.map((prodcut) => { const component = ProductComponent(prodcut); if (prodcut.id === '2' || prodcut.id === '5') { component.actions.push({ name: 'Button', props: { $width: '150px', $margin: '0 0 0 20px', $background: 'firebrick', $color: 'greenyellow', $fontWeight: 'bold', }, children: 'Add to cart', }); } return component; });
After that, our client part will automatically update its appearance:
Or for example, there’s a requirement to style the disabled state for a specific button and add personalized styles for the product name:
Backend Driven UI for Web Development as a CMS Analog
BDUI can also come in handy in web development to make pages and application layouts more responsive. But first, let’s outline the problem: I’ve rigidly coded the website’s page layouts, perhaps using a CMS that contains various fields, text, and images. Later, a requirement came in to make changes, such as adding a new button or field to each conditional block, or introducing a completely new section or new A/B testing variations.
This would require creating many new pages, components, and modifying existing ones, which involves filling out many fields and so on. This is all tedious work that requires a lot of time and effort. On the other hand, using the BDUI approach, I simply need to create components on the client side, while their content, layout, and specifications will come from instructions via an API. This significantly simplifies and speeds up the execution of such tasks.
Conclusion
In my opinion, the Backend Driven UI concept deserves at least your attention and potentially partial adoption in future projects. It opens up many new possibilities for developers by providing a tool for dynamically managing user interface content without the need for regular updates through new versions on digital distribution platforms. It’s worth noting that shifting all business logic to the server side allows us to quickly respond to market trends or user needs.
Major IT giants actively employ BDUI, demonstrating the successful application of this approach in modern market conditions and enhancing user interaction experiences with dynamic interfaces. This is another reason to pay attention to such a powerful tool as Backend Driven UI and consider adding it to your list of skills to master. Skillful application of this approach has the potential to improve both development and user experience with any application.