# 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:

1. **Styles** - Defines the appearance of the component.
2. **Data** - Provides content or values for the component.
3. **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.

```tsx
import { View, Text } from 'react-native';
import React from 'react';

const SimpleComponent = () => {
  return (
    <View style={{ backgroundColor: '#fff' }}>
      <Text style={{ color: '#000' }}>Hello World</Text>
    </View>
  );
};
```

This component always renders **"Hello World"**, and styles are hardcoded.

#### Making the Component Reusable

To improve reusability, we pass **styles and data** as props:

```tsx
const SimpleComponent = (viewStyle: ViewStyle, textStyle: TextStyle, data: string) => {
  return (
    <View style={viewStyle}>
      <Text style={textStyle}>{data}</Text>
    </View>
  );
};
```

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:

```tsx
const SimpleComponent = (style: ViewStyle, children: JSX.Element[], data: string) => {
  return <View style={style}>{children.map((child) => child)}</View>;
};
```

Example Usage:

```tsx
const children = [
  <Text style={{ color: 'black' }}>Hello</Text>,
  <Text style={{ color: 'yellow' }}>World</Text>,
  <Image source={{ uri: 'https://some-logo' }} />,
  <View style={{ backgroundColor: 'red' }} />,
];

<SimpleComponent style={{}} children={children} data={} />
```

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.

```json
{
  "name": "ComponentName",
  "styles": {},
  "components": [],
  "data": {}
}
```

* **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.

```json
{
  "name" : "TabLayoutInflater",
  "styles" : {},
  "components" : [],
  "data" : {}
}
```

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.

```tsx
const CardComponent = () => {
  const [data, setData] = useState(null);

  useEffect(() => {
    fetch("https://api.xyz.com/card").then((data) =>
      setData(data)
    );
  }, []);

  const onPress = useCallback(() => {
    if (data.sectionName.contains("some-string")) {
      navigation.push("route");
    }
  }, [data]);

  return (
    <View style={{ backgroundColor: "#000" }}>
      <Pressable onPress={onPress}>
        <Text> ${data.sectionName} </Text>
      </Pressable>
      <Tab.Navigator>
        <Tab.Screen
          name="First"
          component={<FirstTabComponent data={data.firstTab} />}
        />
        <Tab.Screen
          name="Second"
          component={<SecondTabComponent data={data.secondTab} />}
        />
      </Tab.Navigator>
    </View>
  );
};

const FirstTabComponent = (data) => {}

const SecondTabComponent = (data) => {}
```

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.Navigator
* Pass the styles, data and components to TabLayoutInflater

Here's the updated CardComponent:

```tsx
type ConfigurableProps = {
  style: ViewStyle;
  components: JSX.Element[];
  data?: any;
};

const CardComponent = ({ style, components }: ConfigurableProps) => {
  const [cardData, setUpCardData] = useState(null);

  useEffect(() => {
    fetch("https://api.xyz.com/card").then((result) =>
      setUpCardData(result)
    );
  }, []);

  const onPress = useCallback(() => {
    if (cardData?.sectionName?.includes("some-string")) {
      navigation.push("route");
    }
  }, [cardData]);

  return (
    <View style={{ backgroundColor: "#000" }}>
      <Pressable onPress={onPress}>
        <Text> {cardData?.sectionName} </Text>
      </Pressable>
      <TabLayoutInflater
        styles={style}
        data={cardData}
        components={components}
      />
    </View>
  );
};

const TabLayoutInflater = (props: ConfigurableProps) => {
  const [tabs, setTabs] = useState<Array<Component>>();

  useEffect(() => {
    const c = props.components.map((child) => {
      const Component = getComponent(child.name);
      return (
        <Component
          style={child.styles}
          components={child.components}
          data={child.data}
        />
      );
    });

    setTabs(c);
  }, []);

  return (
    <Tab.Navigator>
      {tabs.map((tab, index) => (
        <Tab.Screen key={index}>{tab}</Tab.Screen>
      ))}
    </Tab.Navigator>
  );
};

const FirstTabComponent = (props: ConfigurableProps) => {};
const SecondTabComponent = (props: ConfigurableProps) => {};
```

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.


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.d11platform.com/flash-client-sdk/understanding-configurability.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
