Part V β The Craft Β· Lesson 10.2
Config Driven UI
In one lineThe server sends JSON describing the screen, and the client renders whatever it's told.
Where you've seen itA food-delivery home screen where the section order changes by city and hour β and nobody shipped an app update to do it.
Diagram
THE PIPELINE
ββββββββββββββ JSON ββββββββββββββββ βββββββββββββββββββ
β Backend β βββββββββΊ β Renderer ββββΊβ Registry β
β (CMS/ β β walks the β β "banner"βBanner β
β config) β β config tree β β "grid" βGrid β
ββββββββββββββ ββββββββββββββββ β "list" βList β
βββββββββββββββββββ
β
βΌ
rendered screen
New arrangement = config change = no deploy
New COMPONENT = code change = deploy (registry entry)THE CONFIG
{ "sections": [
{ "type": "banner", "id": "b1",
"props": { "image": "/diwali.jpg", "cta": "/offers" } },
{ "type": "grid", "id": "g1",
"props": { "title": "Whats on your mind?", "source":
"/api/categories", "columns": 4 } },
{ "type": "list", "id": "l1",
"props": { "title": "Top rated", "source": "/api/top" } }
]}THE RENDERER β small, and the whole idea
function Renderer({ sections }) {
return sections.map(({ type, id, props }) => {
const Component = REGISTRY[type];
if (!Component) return null; β unknown type: skip, never crash
return <Component key={id} {...props} />;
});
}
Forward compatibility rule: an OLD client receiving a NEW type
must degrade silently, not throw.How it works
- Define a schema for the config and validate it at the boundary β an unvalidated config is a runtime crash waiting for a bad CMS entry.
- Keep a registry mapping
typestrings to components; unknown types render nothing rather than breaking the page. - Version the config so old app versions can be served a shape they understand.
- Keep layout in config, behaviour in code. Config that starts carrying conditionals and expressions is becoming a programming language β badly.
- Cache the config with a short TTL (6.7); it's on the critical path for first paint.
Trade-offs
| Gain | Cost |
|---|---|
| Ship layout changes with no release | Type safety weakens at the boundary |
| Per-segment, per-region personalisation | Harder to debug β "why is this here?" |
| Non-engineers can reorder screens | Config becomes a second codebase |
| A/B test layouts trivially (4.4) | Preview/QA tooling must be built |
Where it genuinely pays off: mobile apps behind app-store review, marketing surfaces that change weekly, and multi-tenant products where each customer needs a different arrangement. For a stable internal dashboard it's usually over-engineering.
Interview angle
"How would you build a home screen that changes without a release?"
- Server returns an ordered list of typed sections; the client maps types to components via a registry.
- Validate the config, ignore unknown types for forward compatibility, and version the schema.
- Keep data fetching per section (each declares its own
source) so one slow API doesn't block the whole screen β stream or lazy-load below the fold.
Recap
- Config describes arrangement; code provides capability.
- Unknown types must degrade silently β that's what makes old clients safe.
- Validate and version the config, or you've moved your bugs to a CMS.