Started with: How do you manage complex dynamic forms where input fields appear/disappear based on previous selections
Drive the form from a schema/config, not hardcoded JSX. Compute visible fields from current values, validate conditionally (only visible fields), clear or preserve hidden-field values intentionally, and use a form library (React Hook Form) for performance and field arrays.
Conditional / dynamic forms — fields appearing and disappearing based on prior answers — get unmanageable fast if you hardcode the logic. The senior approach is schema-driven.
1. Describe the form as data
const schema = [
{ name: 'accountType', type: 'select', options: ['personal', 'business'] },
{ name: 'companyName', type: 'text', visibleWhen: (v) => v.accountType === 'business' },
{ name: 'gstNumber', type: 'text', visibleWhen: (v) => v.accountType === 'business', validate: gstRule },
];The render layer just maps over the schema; visibility and validation are config, not branching JSX. Adding a field is a data change.
2. Compute visible fields from current values
On every change, derive which fields are visible. A renderer walks the schema, evaluates visibleWhen, and renders only those. No imperative show/hide spaghetti.
3. Validate only what's visible
A hidden "GST number" field must not block submission. Run validation against the visible set. Schema-driven validation (Zod, Yup) makes this clean — build the active schema from the visible fields, or use .when() / conditional refinements.
4. Decide what happens to hidden-field values
This is the subtle part interviewers probe:
- Clear on hide — usually correct: switching account type shouldn't submit stale business data.
- Preserve on hide — sometimes wanted: user toggles back and forth without re-entering.
Make it an explicit per-field choice, not an accident.
5. Use React Hook Form (or similar)
For anything non-trivial:
- Uncontrolled inputs → far fewer re-renders than a controlled
useStateper field. useFieldArrayfor repeating groups ("add another address").- Built-in validation,
watchfor dependent fields,isDirty/isValidstate.
6. Handle the hard cases
- Repeating groups — field arrays with stable keys.
- Cross-field validation — "end date after start date."
- Async validation — debounced uniqueness checks.
- Multi-step wizards — validate per step, keep state across steps, allow back-nav.
- Server-driven forms — fetch the schema from the backend so forms change without a deploy.
Why schema-driven wins
Logic lives in one inspectable place, the form is testable as data, non-engineers can sometimes own the config, and you stop shipping bugs every time a field's condition changes.
Follow-up questions
- •Should hidden fields keep or clear their values? How do you decide?
- •How do you validate only the currently visible fields?
- •Why are uncontrolled inputs (React Hook Form) better for large forms?
- •How would you handle a form schema delivered by the backend?
Common mistakes
- •Hardcoding show/hide logic in JSX until it's unmaintainable.
- •Validating hidden fields and blocking valid submissions.
- •A controlled useState per field, re-rendering the whole form on every keystroke.
- •Not deciding intentionally what happens to hidden-field data.
Performance considerations
- •Per-field controlled state re-renders the entire form on each keystroke; React Hook Form's uncontrolled approach isolates re-renders to the changed field. Schema evaluation should be cheap — memoize the visible-field computation if the schema is large.
Edge cases
- •A field that's required only when visible.
- •Cascading dependencies — field C depends on B depends on A.
- •Repeating field groups with conditional fields inside them.
- •Restoring a partially-filled form where conditions must re-evaluate.
Real-world examples
- •KYC/onboarding forms where business vs personal changes the whole field set.
- •Insurance or tax forms with deep conditional branching.
- •Server-driven forms (backend ships the schema) so product can change forms without a release.