This post is for Day 16 of Merpay & Mercoin Advent Calendar 2025 , brought to you by @Stefan_droid from the Merpay Growth Platform team.
Introduction
The Growth Platform team in Merpay is responsible for marketing and incentive related development across the entire company. Over the years, we have built an internal customer relationship management (CRM) system, called Engagement Platform (EGP), that allows us to publish campaigns, coupons, and notifications effortlessly to our users and engage with them effectively.
This article explores how we used server-driven user interface (SDUI) architecture to implement a distributable content type called EGP Card within EGP—allowing us to supercharge user engagement while significantly reducing development effort and improving time-to-market.
EGP Card offers a flexible solution that accelerates user engagement across various campaigns and use cases by enabling remote configuration while preserving native performance and aesthetics.
This post will outline the development process, explain how this architectural shift was crucial for enhancing user engagement, and discuss common challenges encountered with such a feature, along with the solutions we devised.
The Traditional Approach & Pain Points
Super apps like the Mercari App offer many opportunities to engage with users and motivate them to take specific actions using incentives communicated through campaigns, usually displayed in various positions within the app.
Back in 2019, we introduced a 3rd-party CRM system into our app. The framework offered only very simple UI components out of the box, so we started to heavily customize our integration. Due to the various designs required for campaigns and their different locations in the app, we couldn’t rely on any default UI components, and we also didn’t have a design system back then. We ended up simply configuring key/value pairs within campaigns on the CRM and delivering them to our campaign areas inside the app.

The UI and business logic of each campaign area had to be implemented separately on every platform. We tried to promote reuse of UI components for campaigns as much as possible, but marketers frequently required changes to adjust the UI to match the next campaign. Over time, the implementations became more and more complex, more and more conditions were required to be configured with the campaign, and the apps needed to deal with all kinds of configuration combination variations. This increased the risk of incidents and the time the QA team would require to confirm the functionality of a feature. Also, for each change or bug fix, another app release was required, which made the process very time-consuming. In conclusion, creating a new component and applying changes to existing components would require almost the same amount of time and effort.

With the introduction of a Design System in 2021, we believed that would help with the standardization of marketing-related UI components, promote reusability, and reduce implementation efforts. In practice, however, marketers still frequently needed solutions beyond what any standardization or design system could offer to engage users effectively, leaving our pain points unaddressed.
What is Server-Driven UI?
This section contains an introduction to Server-Driven U. If you are already familiar with the concept, you can move on to the next section.
Server-Driven UI (SDUI) is an architectural pattern where the server dictates the structure and content of the user interface, rather than the client application (like a mobile app or web front-end) having its UI hardcoded.
The key mechanics of SDUI are:
- The Server sends data and layout instructions: The server responds to a client request with a JSON payload (or similar format) that describes which UI components to render, their properties (text, color, image URLs), and their arrangement.
- The Client renders UI dynamically: The client application (e.g., iOS, Android, or Web) acts as a universal renderer. It reads the server’s instructions and dynamically constructs the UI using its often pre-defined set of components (often based on a Design System).
- Decoupling UI configuration from client deployment: SDUI separates the UI structure and content from the client application code itself, allowing product teams to update layouts, flows, and content by changing the server response without requiring a new client application release or app store approval.

SDUI in Action: Reducing Time-to-Market
We adopted the power of SDUI and created a feature called "EGP Card," which is one of the content types configurable for campaigns that can be delivered by our internal CRM Engagement Platform (EGP) to the client applications. This allowed us to include this new approach alongside existing alternative content types, like the previously frequently used hard-coded UI components, to quickly make it available to all clients and use already existing tooling like EGP’s WYSIWYG editor to design EGP Cards in a visual editor without spending additional effort building such an editor first.

Initially, to become production-ready, the effort required was quite high to build client-side renderers for all platforms that could render the JSON schema reliably. However, as a result, we were able to optimize our release workflow for new campaigns significantly.

Compared to the traditional approach, EGP Card’s integration is slightly faster for completely new campaign areas because of the standardized implementation that can be reused across different screens. The effort of implementing each UI element and aligning with design is unnecessary during the development phase, because the UI will be created in the web editor. EGP Card consists of a single placeholder view that renders native UI during runtime, so only the implementation of this single view and business logic is required as initial setup.
Once the campaign area is implemented, changes to the UI due to new requirements can be made completely remotely and no longer require client-side implementations or waiting time for the next app production release. As a result, the most time-consuming part becomes the finalization of specifications and design. Creating a new EGP Card or applying changes to existing ones can be done easily by drag-and-drop in the web editor and takes only a few minutes.
With this approach, it became extremely easy for us to conduct A/B tests with several variants and test which UI works best to engage with our users. New releases and updates can be published with a single button click instantly. This allows us to be flexible and react to issues immediately.
Implementation Challenges & Solutions
While Server-Driven UI offers substantial benefits in reducing time-to-market, the path to a robust and scalable SDUI system is not without its hurdles. Our experience highlighted several key challenges and led us to the solutions described below.
Versioning and Backwards Compatibility
A major risk with SDUI is introducing a new component or changing a schema in a way that breaks rendering on older client application versions that are still in use.
Solution: Client-side renderer and the overall SDUI schema are rigorously versioned.

- Graceful Degradation: Client renderers are aware of their latest supported schema version and are built with robust error handling to skip rendering of schemas with higher versions to avoid application crashes. Even when unsupported components are accidentally served to an old app, the renderers will catch them during the parsing process, so that the core functionality remains stable.
- Server-Side Logic: The server identifies the client’s renderer version in the request and only serves content that the specific client renderer version is known to support. Our editor allows us to specify the schema version to optionally provide different schemas to specific renderer versions, such as those used in older versions of the application.
Rendering Differences Between Platforms
The decision to go with native code client-side rendering engines for our SDUI solution came with a high cost of building specific rendering engines for all platforms that we support (Android, iOS, Web, Flutter). This was a great challenge for the team to ensure that each platform would respect and interpret all styling properties and components in the same way to ensure consistent rendering on each device.
Spoiler: We did encounter several problems with inconsistent UI rendering.
Solution: Gradual improvements, detailed documentation, and thorough testing.
- Fixes & Documentation: No product is perfect from the beginning, so we continued to improve our solution and documentation to specify behavior more precisely over time.
- Unit & Screenshot Testing: Before even the first production release, we created a base set of unit and screenshot tests for the base components and styling. Modern frameworks like Jetpack Compose and SwiftUI make it very easy to build and test UI, and I think that’s why SDUI has become more popular again in recent years. Thorough testing was very important as it gave us and other stakeholders confidence in our solution. We started to share JSON test cases between the platforms to ensure that our logic and behavior were aligned.
- Automation: Several changes and improvements over time can easily cause regressions. To avoid that, we integrated our screenshot testing into our CI/CD workflow. Furthermore, our team built tooling that allowed us to compare all platforms directly with each other to quickly discover differences (see screenshots below).
- Utilize AI: AI is a great help for finding issues, improving rendering logic, and creating comprehensive test cases. For Mercari Hallo, an app built with Flutter, we even created a native Dart plugin to support EGP Card instead of building a plugin using native Android and iOS channels in the background. The reason for that was a mix of dependency issues, complexity, and a tight deadline by which we needed it. Luckily, building an additional renderer becomes, thanks to AI, a very easy task. Agents can quickly understand the logic used and generate code from one language into another, and then use existing test cases to validate the logic and the rendered UI output.

Design System integration
One of the most frequently asked questions to our team was whether our solution uses our internal Design System, and people were surprised when we answered that we didn’t, at least not directly. The main reason is that the requirements we receive from marketers often don’t align with the Design System, and building a solution purely on the Design System would result in us adding more and more exceptions to satisfy the requirements.
Solution: We decided to make the SDUI styling as granular as possible and make it easy to use with our existing What-You-See-Is-What-You-Get (WYSIWYG) web editor. This option gives us the most freedom but also adds more complexity to the rendering engine. However, since we are always striving for improvements and making processes easier, we are currently planning to integrate the Design System components into our web editor by automating the generation of component-level templates using an AI agentic workflow to publish them based on our Design System.
Personalization
Marketing often wants to engage with our users as personally as possible and create personalized experiences that are most valuable to them. As a basic example, instead of just showing a generic campaign about a clothes-related coupon: "Save 20% on clothes products," we want to personalize the experience and show a liked item of the user from the clothes category and display how the price would change for them if they used the coupon. This method is much more effective and more meaningful for the user.
Solution: Introducing placeholders and compose final schema on backend
We decided on a simple {{ key }} schema that can be used across the web editor to replace predefined keys on our backend side. During the API request from the client, the backend fetches the static schema for the EGP Card from the CDN and then replaces all the placeholders with aggregated data for the specific user. This approach simplifies the client-side renderers, keeping them "dumb" and eliminating the need for complex replacement logic. The backend and web editor frontend require some kind of contract to understand which placeholders are available. Currently, we are relying on documentation to achieve this, but this could be further improved by using for example Protocol Buffers (Short: Protobufs – Google’s language-neutral data serialization format) to have a single source of truth and add new placeholders automatically.
State Management and Interactive Components
One frequent feature request is the possibility of adding interactive components to EGP Cards, such as a Like button. But even a simple Like button can become really complex when trying to design a feature that is scalable for more than a single use case. Let’s take a closer look at it:
Example requirements for a Like button:
- Show different UI based on the like state (liked, not liked)
- When the user taps the Like button, the state should change and trigger an asynchronous API request to the backend to persist this information
- When the API request fails, the state should return to ‘not liked’
Suddenly, our SDUI features need to be able to maintain a state and make API requests. These two requirements are not trivial to address in our so far static JSON schema. It might be arguable whether such a feature belongs in a SDUI solution or whether stateful logic should rather be implemented natively.
There is probably no single best solution to this problem, but there are several approaches to deal with it.
Possible Solutions:
- Custom-Components: Probably the most common solution with the least impact on the existing schema. Simply create a new component that can be selected which contains a reference ID. The client will replace the component based on the reference ID with a hard-coded component defined on the client side, which already holds all the business logic and state management that are required. Each client needs to implement the component individually; otherwise, clients wouldn’t be able to display it. Using a custom component makes it difficult for the web editor to preview its appearance unless a specific preview for each component is provided.
{
"type": "Custom",
"referenceId": "IconLikeButton"
}
// Android Compose Component Example
EgpCardView(
egpCard = card,
isDarkMode = state.isDarkMode,
onDisplay = { ... },
onClick = { ... },
onNavigate = { ... },
customComponent = {
when (it) {
"IconLikeButton" -> IconLikeButton()
}
}
)
- Stateful Wrapper-Component: Another solution would be to create a new stateful wrapper component that is able to maintain a state and share it with its child components down the component tree. It would also have knowledge about the API endpoint and everything that is required for the client to compose a valid API request. Based on a successful or failed response, this stateful wrapper component could adjust its state and control the UI. This approach requires adding a very complex new component to the schema and might not work with non-REST-based endpoints.
{
"type": "StatefulWrapper",
"stateRef": "state1",
"states": {
"init": {
"id": "clickable_element",
"type": "IconButton",
"actions": {
"onClick": [
{
"type": "API/REQUEST"
}
]
},
"styles": { ... },
"children": [ ... ]
},
"loading": null,
"error": { component },
"success": { component }
},
"api": {
"url": "/api/endpoint",
"method": "POST",
"data": {},
"headers": {},
"onSuccess": {
"type": "setState",
"stateRef": "state1",
"value": "success"
},
"onError": {
"type": "setState",
"stateRef": "state1",
"value": "error"
}
}
}
Both solutions have their advantages and disadvantages. We are still considering which approach fits best for us, and there might even be a better solution.
Generate UI from Figma design using AI
Despite using a web editor for UI creation, which enables continuous deployment, the development of the UI components themselves still takes place within the editor. To significantly shorten the cycle from design concept to deployable components, we investigated the use of modern AI models to assist in this process.
Solution: Initially, we considered utilizing AI models and MCP to generate our schema based on design tokens from Figma. However, we require consistent output based on the same input data, and most modern AI models do not offer the ability to control their temperature (setting to control randomness in LLMs). Therefore, we decided to develop a plugin for Figma instead to get deterministic results. Designers could define a new component in Figma, and the plugin would automatically generate a preliminary schema based on the design tokens and structure. This schema could then be imported directly into the web editor. While it doesn’t fully automate the process, it significantly reduces the manual effort required for the initial implementation of a new component.

Conclusion
SDUI solutions require a lot of initial effort and some risk-taking to invest the time to achieve a production-ready solution. Our Growth Platform engineering teams believed in this solution, which helped us gain trust and resources to build it. Now we frequently get inquiries from other teams asking us about the feasibility of building their features with our solution. This helps us improve and extend it further and MOVE FAST together. Modern frameworks, languages, and increasing internet speed significantly contribute to the success of SDUI solutions, and we can see that this will become increasingly relevant technology in the future, especially for marketing, where speed and flexibility are key.



