OM
EN ES

Situation

Across different client apps and CMS hosts, teams kept rebuilding similar form flows with bespoke validation and host shells that learned about every keystroke—slow to deliver and expensive to change.

Task

Apply a consistent layering mindset—schema, engine-or-state, and field UI—chosen per product or CMS, so form lifecycle stayed local and host-specific concerns stayed at the edge.

Action

On CMS surfaces, favored a stable form schema plus host adapters. On product apps, used a contract or typed-builder schema with a thin field UI. Reused patterns (conditional visibility, async validation, submit pipeline, dumb shells) only where that host needed them—not all at once in one app.

Result

Form-heavy work assembled faster when reuse justified a schema; tiny or hyper-custom forms stayed hand-built. Re-render noise dropped because shells stopped subscribing to every keystroke.

System shape

Same criteria of layers across products; different stacking per host:

| Pattern | When it fit | |---------|-------------| | A — Schema contract + engine + dumb UI | Product apps that needed a clear form brain and presentational fields | | B — Typed builders + thin engine + DS fields | Teams that wanted stronger typing without a heavy form library lock-in | | C — Schema + CMS/host adapters | Sanity / HubSpot / Drupal-style hosts where the edge changes but the form idea should stay portable |

Important: this describes several apps/CMS, each picking one stacking—not one app running A, B, and C together.

Decisions

CMS surfaces → schema + adapters (C)

Chose: a stable form schema plus a host adapter for payloads, errors, and ownership of schema changes. Rejected: putting CMS APIs, content-model details, or embed lifecycle inside field UI or the layout shell. Why: CMS hosts change; the form should remain portable. The adapter absorbs the edge.

Product apps → A or B, not a forced CMS model

Chose: contract schema + engine (A) or typed builders + thin engine (B), depending on the team. Rejected: dragging the CMS-adapter stacking into an app that did not need it—or forcing an app engine into a CMS. Why: architecture is a per-product decision. Copying another host’s stacking by habit creates the coupling this case exists to avoid.

Patterns that paid off

Used across apps/CMS as needed (again: chosen per surface):

  • Conditional visibility — steps/conditions for onboarding or marketing CMS flows.
  • Async validation — when the host/API owned the check; UI only displayed the error.
  • Submit pipeline — especially on CMS: engine builds payload → adapter delivers → UI never knows the endpoint.
  • Dumb shell — form state does not subscribe the layout/CMS shell to every keystroke.

For engineering-manager skim: on CMS hosts, submit pipeline + dumb shell carried the most weight; visibility and async validation appeared when the form earned the complexity.

When not to use this

Schema-driven layering was the wrong tool when:

  • The form was a one-off / hyper-custom UX—the schema cost more than it saved; build the UI by hand.
  • The form was tiny (about 2–3 fields)—ceremony without reuse; wire design-system inputs and submit directly.

What we refused

  • Validators living only in the UI (duplicated and hard to reuse)
  • Layout or CMS shells re-rendering or subscribing on every keystroke
  • CMS endpoint / content-model logic inside field components

Transferable lesson

Primary: Isolate the form’s lifecycle from the host shell. If the layout learns about every keystroke, you already lost.

Supporting rules:

  • Validators and the CMS edge do not live in field UI—the UI paints; schema, engine, or adapter decides.
  • Pick stacking (A/B/C) per app or CMS; do not copy another product out of habit.
  • Go schema-driven when there is reuse or real complexity; go hand-built when the form is minimal or hyper-custom.

Limits of this write-up

Anonymized across clients and hosts. No proprietary field schemas, internal endpoints, or brand names. Deeper multi-CMS contract narrative belongs with the multi-CMS case; this case stays on form layering and state isolation. Client work under NDA — architecture and decisions shown, product details withheld by design.