Skip to main content

Config Driven UI

Config-driven UI means generating an interface from configuration and data instead of manually hardcoding every section.

Config + Data
↓
Rendering Logic
↓
Reusable Components
↓
Dynamic UI

One-line idea: The configuration describes what to render, while reusable components decide how it appears.

Core Concepts​

The Problem with Hardcoded UI​

A small interface may begin with manually repeated components:

<Card />
<Card />
<Card />

As the application grows, this approach creates problems:

  • repeated markup increases
  • layouts become harder to manage
  • changing section order requires code changes
  • customization becomes difficult
  • supporting multiple UI variations takes extra effort

Config-driven UI moves the repeated structure into data:

const sections = [
{ id: "one", type: "card" },
{ id: "two", type: "card" },
{ id: "three", type: "card" },
];

The frontend reads the configuration and renders the matching components.

What Is UI Configuration?​

A UI configuration is a data structure that describes the sections of an interface.

const pageConfig = [
{
id: "hero",
type: "banner",
data: {
title: "Summer Collection",
description: "Explore the latest products",
},
},
{
id: "recommended",
type: "scrollableRow",
title: "Recommended for You",
data: recommendedProducts,
},
];

The configuration can control:

  • component type
  • content or data
  • section order
  • enabled or disabled sections
  • component variation
  • layout selection

Config and Data​

Config and data have related but different jobs.

ValueResponsibility
typeSelects the component to render
dataSupplies the content displayed by that component
variantChooses a visual or behavioral variation
enabledControls whether the section is shown
Array orderControls the order of sections

Example:

{
type: "scrollableRow",
variant: "movie",
enabled: true,
data: movies
}
type     -> render ScrollableRow
variant -> use movie-style cards
enabled -> show the section
data -> display the movie items

Type-Based Rendering​

The type field is the core of the rendering decision.

type = "card"          -> Card
type = "banner" -> Banner
type = "scrollableRow" -> ScrollableRow

A simple renderer can use conditional logic:

SectionRenderer.jsx
export default function SectionRenderer({ section }) {
if (section.type === "banner") {
return <Banner data={section.data} />;
}

if (section.type === "scrollableRow") {
return (
<ScrollableRow
title={section.title}
data={section.data}
variant={section.variant}
/>
);
}

if (section.type === "card") {
return <Card data={section.data} variant={section.variant} />;
}

return null;
}

Different configuration values produce different UI without manually rewriting the page.

Independence and Flexibility​

When the interface depends on configuration:

  • sections can be reordered
  • layouts can change
  • sections can be enabled or disabled
  • the same component can display different data
  • one rendering system can support multiple UI variations
Fixed page structure
↓
Configurable rendering system
↓
More freedom to change the interface

The frontend becomes a reusable system rather than a collection of fixed pages.

Practical Config-Driven Patterns​

Build a Page Renderer​

The page renderer loops through the configuration and delegates each section to a section renderer.

PageRenderer.jsx
export default function PageRenderer({ config }) {
return (
<main>
{config.map((section) => (
<SectionRenderer key={section.id} section={section} />
))}
</main>
);
}
HomePage.jsx
const homePageConfig = [
{
id: "main-banner",
type: "banner",
data: {
title: "Discover New Arrivals",
description: "Fresh products selected for you",
},
},
{
id: "popular-products",
type: "scrollableRow",
title: "Popular Products",
variant: "product",
data: popularProducts,
},
];

export default function HomePage() {
return <PageRenderer config={homePageConfig} />;
}

Rendering flow:

HomePage passes config
↓
PageRenderer reads every section
↓
SectionRenderer checks its type
↓
Banner or ScrollableRow renders

Use a Component Registry​

As the number of section types increases, a component registry keeps the mapping in one place.

sectionRegistry.js
export const sectionRegistry = {
banner: Banner,
card: Card,
scrollableRow: ScrollableRow,
};
SectionRenderer.jsx
import { sectionRegistry } from "./sectionRegistry";

export default function SectionRenderer({ section }) {
if (section.enabled === false) {
return null;
}

const Component = sectionRegistry[section.type];

if (!Component) {
return null;
}

return <Component {...section} />;
}

The registry connects configuration types to reusable React components.

"banner"        -> Banner
"card" -> Card
"scrollableRow" -> ScrollableRow

Config-Driven Scrollable Row​

A scrollable row is reusable because its content comes from configuration.

const movieRowConfig = {
id: "trending-movies",
type: "scrollableRow",
title: "Trending Movies",
variant: "movie",
data: movies,
};
ScrollableRow.jsx
export default function ScrollableRow({ title, data, variant }) {
return (
<section className="scrollable-row">
<h2>{title}</h2>

<div className="scrollable-row__items">
{data.map((item) => (
<Card key={item.id} data={item} variant={variant} />
))}
</div>
</section>
);
}

The same component can render:

  • products
  • movies
  • recommendations
  • donation cards
  • dashboard content

Only the configuration and data need to change.

Multiple UI Variations​

One reusable component can support multiple presentations through a variant value.

<Card variant="movie" data={movie} />
<Card variant="product" data={product} />
<Card variant="donation" data={campaign} />

The variation can also come from configuration:

const section = {
type: "scrollableRow",
variant: "donation",
data: donationCampaigns,
};
Card.jsx
export default function Card({ data, variant }) {
return (
<article className={`card card--${variant}`}>
<img src={data.image} alt={data.title} />
<h3>{data.title}</h3>

{variant === "product" && <p>₹{data.price}</p>}
{variant === "movie" && <p>{data.rating}/10</p>}
{variant === "donation" && <p>{data.progress}% funded</p>}
</article>
);
}
Same component structure
+
Different variant and data
↓
Different UI presentation

Enable or Disable Sections​

Configuration can control whether a section is visible.

const pageConfig = [
{
id: "offer-banner",
type: "banner",
enabled: false,
data: offer,
},
{
id: "products",
type: "scrollableRow",
enabled: true,
data: products,
},
];

The renderer ignores disabled sections:

if (section.enabled === false) {
return null;
}

This changes the visible page without rewriting its JSX structure.

Change Section Order​

When a page is rendered from an array, the array order becomes the UI order.

const firstLayout = [bannerSection, productSection, movieSection];

const secondLayout = [movieSection, bannerSection, productSection];
Config order changes
↓
Rendered section order changes

This makes page layouts more customizable.

Data-Driven Updates​

Components do not need to be manually rewritten when their content changes.

const config = {
type: "card",
data: {
title: "Old title",
},
};

When data.title changes, the same card component renders the new title.

Config changes
Data changes
↓
React receives new props
↓
The UI updates

Benefits, Trade-Offs and Common Mistakes​

Benefits

  • reduces manually repeated page markup
  • makes section ordering flexible
  • supports reusable components and layouts
  • allows sections to be enabled or disabled
  • supports multiple visual variations
  • separates page structure from component rendering
  • makes large interfaces easier to customize

Trade-offs

Simple hardcoded UIConfig-driven UI
Direct and easy for a very small fixed pageBetter for repeated or changeable layouts
Structure is visible directly in JSXStructure is split between config and renderer
Changes require editing the pageMany changes can be made through config
Repetition grows across similar pagesReusable renderers reduce duplication

Config-driven UI is most useful when several screens share components or when content, order and variations change. A small fixed screen may not need the extra rendering layer.

Common mistakes

MistakeBetter approach
Hardcoding every repeated sectionStore repeated structure in configuration
Rendering all types in the page componentUse a dedicated section renderer
Using one component for every possible layoutCreate focused reusable components and variants
Mixing content directly into rendering conditionsKeep content in data
Ignoring unknown type valuesReturn a safe fallback or skip the section
Repeating long if chains as types growKeep type-to-component mappings in a registry
Forgetting stable keys for configured sectionsGive every section a stable id

Summary​

Config-driven UI turns reusable React components into a flexible rendering system controlled by configuration and data.

Page configuration arrives
↓
PageRenderer loops through sections
↓
SectionRenderer reads each type
↓
Registry selects a reusable component
↓
Data and variant become component props
↓
Components render the final UI