Context
AppCMS settings span 34 namespaces, each containing a variable number of form fields with heterogeneous types, conditional visibility rules, and grouped layouts. The prior approach required each namespace to maintain its own bespoke page component, resulting in duplicated form wiring, inconsistent layout, and high cost-per-namespace for onboarding new settings.
A decision was needed on how to render settings UI from configuration rather than hand-authored page components so that adding a namespace becomes a data concern, not a UI engineering effort.
Decision
Adopt a config-driven render engine as the single rendering path for all AppCMS settings namespaces.
-
Typed config per namespace — Each namespace declares a
SectionConfig, a typed JSON-compatible structure that groups fields intoSectionItemblocks. -
Normalizing adapter —
normalizeRawConfigconverts raw API config payloads into the typedSectionConfigshape, inferring field types from value shapes when explicit types are absent. -
Field-component registry — The render engine delegates each field to a registry (
FieldType → React component) supporting eight types:switch,text,number,textarea,select,combo,dataMap, anddivider. -
12-column grid layout — Layout is expressed via CSS grid with per-item
spancontrols. Conditional visibility is resolved at render time from live form values usingshowpath expressions evaluated vialodash/get. -
Lazy loading — Namespace configs are loaded through a lazy registry where each entry is a
() => import(...)loader, so webpack code-splits every config into its own chunk. Config resolution and settings data fetch are parallelised at the consumer.

Consequences
-
Enables
- Adding a new namespace by authoring a single config file and one registry line — no new React components required
- Deterministic, testable rendering: adapter normalization and conditional visibility are pure functions with full unit coverage
- Bundle efficiency: only the config chunk for the opened namespace is loaded
- Consistent layout and interaction patterns across all 34 namespaces without per-namespace divergence
-
Restricts
- All settings UI must flow through the render engine; namespace-specific one-off components are not permitted
- New field types require a registry entry and a corresponding field component before they can be used in configs
- Layout is constrained to the 12-column grid model; freeform positioning is intentionally unsupported
-
Trade-offs accepted
- Highly custom namespace UIs (if ever needed) would require extending the engine rather than bypassing it
- The normalizing adapter carries inference heuristics that may need updating if backend config shapes evolve significantly
Notes
- Related module docs:
docs/modules/uploader/(follows a similar config-driven pattern for upload workflows) - The adapter and render engine are covered by dedicated Jest suites under
src/__tests__/ - Namespace rollout is incremental — the registry uses
Partial<Record>so unconfigured namespaces gracefully resolve tonull