Understanding Config-Driven UI: A Beginner’s Guide with React


Config Driven UI is a design pattern where the structure and behavior of the user interface are determined by configuration files like JSON or XML, rather than being hard-coded within the application. This approach allows developers to easily define the UI layouts, components, and behaviors through an external file, making it easier to modify or extend it by making changes in the configuration file.
Relevance and Benefits of Config-Driven UI in Modern Web Development
Flexibility and adaptability: One of the key advantages of a Config-Driven UI is its flexibility. Users can tailor the user interface to meet specific needs, whether those needs are user-specific or role-specific. For instance, an admin might have access to different UI components compared to a regular user. Additionally, users can apply custom themes to their UI by simply modifying the configuration file. This means that changes can be made dynamically without altering the underlying code base, allowing for a more personalized and adaptable user experience.
Maintainability: Another significant benefit is maintainability. By separating business logic from UI logic, developers can achieve a cleaner and more readable codebase. This separation simplifies the process of updating the UI. Instead of hunting through scattered code to make changes, developers can update the configuration file to implement new features or modify existing ones. This approach not only reduces the risk of introducing bugs but also makes it easier to manage and scale the application over time.
Reusability: The Config-Driven UI enables the reuse of UI components across various sections of the web application with different configurations. This reusability allows us to maintain a consistent user interface throughout the entire application by utilizing shared configuration files. For example, a button component can be configured differently for different pages, such as having different labels, colors, or actions, while still using the same underlying code. This not only saves development time but also ensures a uniform look and feel across the application. Additionally, it simplifies the process of making global updates to the UI, as changes to the shared configuration file will automatically propagate to all instances where the component is used. This approach enhances both the efficiency and consistency of the development process, making it easier to manage and evolve the application over time.
Empowerment for Non-Devs: Allows the UI designers or product managers to modify the UI without writing code in the react codebase and enables rapid prototyping of new features, UI layouts and components.
Implementing Config-Driven UI in react
Now let's see how we can implement config-driven UI in react with a simple example.
First, let's create a project in React using Vite by running the following line in the terminal.
npm create vite@latest config-driven-ui-example --template react
now navigate into the project directory and install the packages
cd config-driven-ui-example
npm install
now open the project in your text editor/IDE and go into App.jsx
make sure you remove all the extra code and define an object called componentsMap
and add two properties input and button and assign arrow functions to these properties which takes props as parameters and returns a JSX component which uses the props. in our case, for input we will add a label with text, type of input, and placeholder all coming from the props object.
You can give styles to individual components in the componentsMap
like I did in the input component by giving display: 'block'
to keep label and input on same line separately
const componentsMap = {
input: (props) => (
<div style={{ display: "block" }}>
<label>{props.label}</label>
<input type={props.type} placeholder={props.placeholder} />
</div>
),
button: (props) => <button onClick={props.onClick}>{props.label}</button>,
};
function App() {
return (
<div>
// code
</div>
);
}
export default App;
Now In App.jsx
Add a new functional Component named ComponentByConfig
which takes config
as prop. We are going to write config within a object in a separate file with an attribute components
like { components: [ { component1 }, { component2 }, ... {component n} ] }
So we will De de-structure the components property from the config prop.
function ComponentByConfig({ config }) {
const { components } = config;
return (
<div>
// code
</div>
);
}
Now perform the map function on the components
array to render every component defined in configuration by determining the type attribute of every component and passing its props to the component.
function ComponentByConfig({ config }) {
const { components } = config;
return (
<div>
{components.map((component, index) => {
const Component = componentsMap[component.type];
return <Component key={index} {...component.props} />;
})}
</div>
);
}
Now lets create a config.js
file in our react project
In config.js
create an object config
which contains an array of components within its components
property.
const config = {
components: [
{
type: "input",
props: {
type: "text",
label: "Username",
placeholder: "Enter Username",
},
},
{
type: "input",
props: {
type: "email",
label: "Email ID",
placeholder: "john@example.com",
},
},
{
type: "button",
props: {
label: "submit",
onClick: () => alert("Submitted!"),
},
},
],
};
export default config;
In the components attribute, we have added four components where 2 are inputs (name, email) and the last one is a button with the onClick event attribute which can be assigned with an arrow function.
The type attribute is used while determining the type of input from the componentsMap
and the props are properties passed to the components.
Now import this config in App.jsx
and in App
component, render the <ComponentByConfig />
component and pass the config as a prop.
import config from "./config";
const componentsMap = {
input: (props) => (
<div style={{ display: "block" }}>
<label>{props.label}</label>
<input type={props.type} placeholder={props.placeholder} />
</div>
),
button: (props) => <button onClick={props.onClick}>{props.label}</button>,
};
function App() {
return (
<div>
<ComponentByConfig config={config} />
</div>
);
}
function ComponentByConfig({ config }) {
const { components } = config;
return (
<div>
{components.map((component, index) => {
const Component = componentsMap[component.type];
return <Component key={index} {...component.props} />;
})}
</div>
);
}
export default App;
The code above results in output like this with the config:
Now if we want to add a password field in this form, we can simple add this by adding a input of type password in config.js
as shown below:
const config = {
components: [
{
type: "input",
props: {
type: "text",
label: "Username",
placeholder: "Enter Username",
},
},
{
type: "input",
props: {
type: "email",
label: "Email ID",
placeholder: "john@example.com",
},
},
/* -------- Password Field -------- */
{
type: "input",
props: {
type: "password",
label: "Password",
placeholder: "Enter Password",
},
},
{
type: "button",
props: {
label: "submit",
onClick: () => alert("Submitted!"),
},
},
],
};
export default config;
Resulting in This:
Conclusion:
In conclusion, adopting a Config-Driven UI approach in React offers numerous benefits, including enhanced flexibility, maintainability, and reusability. By defining the UI structure and behavior through configuration files, developers can create adaptable and personalized user experiences without altering the core codebase. This method also empowers non-developers to make UI changes, facilitating rapid prototyping and iteration. As demonstrated in our example, implementing a Config-Driven UI in React is straightforward and can significantly streamline the development process, making it easier to manage and scale applications over time.
Subscribe to my newsletter
Read articles from Adwait Rao directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by