Understanding Configurability
Introduction
Before diving into how Server-Driven UI frameworks define components and their DSL (Domain-Specific Language), it's important to understand the concept of configurability.
In traditional app development, developers make components reusable by exposing props, styles, and behaviors allowing the same component to be used in different contexts with different configurations.
Flash builds on this same principle. The component constructs and DSL used in Flash are a direct extension of how we already think about reusable components in code. Instead of defining configurations in code, we now define them as structured data (typically JSON) that can be interpreted by the app at runtime.
By understanding configurability, you’ll better appreciate how server-driven components are designed, how much flexibility they offer, and how product teams can control UI behavior without additional development effort.
In the following sections, we’ll explore configurability through simple examples. We’ll start by creating a basic UI component, gradually make it configurable, and then use that foundation to define the Flash component construct and ultimately the DSL that powers it.
Component in React Native
React Native components require three fundamental properties to render:
Styles - Defines the appearance of the component.
Data - Provides content or values for the component.
State - Manages dynamic changes in the component.
This document explores how to define a Domain-Specific Language (DSL) for Server-Driven UI (Flash), the reasoning behind its structure, and how to build a renderer capable of dynamically interpreting and rendering components.
But before we dive into Flash, let’s step back and look at how we build configurable components in our codebase today. We’ll start by implementing a basic SimpleComponent
that renders static text, and then gradually enhance it to support configurability.
This component always renders "Hello World", and styles are hardcoded.
Making the Component Reusable
To improve reusability, we pass styles and data as props:
Now, the component can render any text with configurable styles.
Supporting dynamic child components
The above component is reusable and supports configurable styles and data. But we can take it a step further by making the component’s children dynamic, allowing even greater flexibility and composition.
To allow rendering child elements dynamically:
Example Usage:
The `SimpleComponent` is now reusable and supports configurable styles, data and children.
In other words, we can also say that for a component to be configurable it should supports configurations for styles, children and data.
Such configurable component should take following three parameters as input:
styles: Applied to the root component.
children: Elements to be rendered inside the component.
data: Dynamic values for the component.
Supporting Server-Driven Configuration
Earlier, we discussed how to make components configurable in the codebase. During that process, we established a fundamental principle: configurable components should accept styles, children, and data as inputs.
When these configurations are supplied by a server, we refer to the component as server-driven. Flash compliant components must adhere to this construct and take styles, children and data as inputs.
To support server-driven configurations, the app must fetch these inputs from a remote source. For the app to correctly interpret and render these configurations at runtime, they must adhere to a predefined contract between the server and the mobile client. This contract is known as the DSL (Domain-Specific Language).
On the client side, the mobile app requires an interpreter and renderer that can understand this DSL and dynamically render the appropriate components on the device.
DSL
DSL is JSON representation of the component construct. It contains key and values which are necessary to define server driven components. Below is the basic version of Flash DSL.
name: React Native component name (e.g., View, Text, Image) or a custom component.
styles: Styling properties for the component.
data: Dynamic content for the component.
components: Child components, following the same structure recursively.
Handling Complex Layouts with Inflaters
Real-world mobile apps often involve complex layouts such as Tabs, Bottom Tabs, and Lists. In React Native, both simple elements like Text
and complex structures like Tabs
are treated as components. What makes components like Tabs
special is that they understand how to render their children—for example, Tabs
will always lay out its children in a tabular format.
Similarly, Flash handles complex layouts using the concept of special components that know how to interpret and render their children. In Flash, these special components are called Inflaters.
As Inflaters are also components, they follow the same server driven component construct.
A DSL representation for a TabLayout might look like this:
TabLayoutInflater
is a component which renders components given input to it in Tabular format.
By introducing Inflaters
, we enable the DSL to define not just how components look, but how they behave making complex layouts configurable without changing app code.
Business Components
The Flash DSL and component construct allow developers to configure a component’s styles, hierarchy, and data. With the help of Inflaters, even complex layouts can be defined in a server-driven manner.
However, in real-world mobile apps, components often encapsulate business logic, state management, and user interactions. The current Flash DSL does not support configuring business logic directly. But what if these logic-heavy components also need to be configurable in terms of styles, data, and children?
The Flash framework is designed with this in mind. Components that include business logic but still require configurability are supported using the same Flash component construct. We refer to these as Business Components
or Pre-Baked Components
.
Let’s understand this with an example.
Consider an CardComponent
, which includes business logic such as fetching data via an API and updating internal state on a button click. It also contains a Button
and a TabNavigator
as children.
While the Flash DSL doesn't (yet) support configuring the business logic itself, it does support configuring the JSX structure of the CardComponent
. To make this possible, the component must follow the Flash component construct—that is, it should accept styles, children, and data as input.
Lets make CardComponent
Flash compliant. It can be done in following steps
Update the method signature to follow the flash component construct
Use
TabLayoutInflater
in-place of Tab.NavigatorPass the styles, data and components to TabLayoutInflater
Here's the updated CardComponent:
The CardComponent can now have capabilties
Re-order Tabs FirstTabComponent, SecondTabComponent
Add new Tab
Change Title of Tab
Change styles of Tab
Configurability in Flash
Flash is designed to offer maximum flexibility to developers. It allows them to choose the level of configurability that best fits their needs. Developers can make an entire screen configurable, or just a specific section of the screen. They also have full control over how deeply nested the configurability should go whether it’s at the screen level, component level, or within individual child components.
Last updated