# Build a Multi-Step Form Wizard

Source: https://www.techinterview.org/post/3233475201/build-multi-step-form-wizard/
Updated: 2026-07-26 · techinterview.org

Wizards (multi-step forms) appear in checkout flows, onboarding, signup, settings. [The interview](/system-design-interview-guides/) tests whether you understand cross-step state management, validation strategies, and the UX details that make wizards feel polished vs frustrating.

## Functional requirements

- Multiple steps, navigable in sequence — model steps as data (an array of step configs), not hardcoded JSX, so adding or reordering a step is a one-line change.

- Validation per step (cannot advance with errors) — validate only the current step's fields on Next; don't block the user with errors for fields they haven't reached yet.

- Step indicator (1 of 5) — showing where the user is and how much remains measurably reduces abandonment on longer flows.

- Back button to return to previous step — going back must never wipe entered data, which is the most common trap interviewers set on this question.

- Save partial progress (e.g., refresh-tolerant) — persist state so a refresh or accidental tab close doesn't force a restart; localStorage covers most cases, a server draft for high-value flows.

- Submit only after final step — nothing hits the real endpoint until the last step; earlier steps stay client-side or write to a draft.

## State architecture

Single source of truth for all form data. Pass through context or a shared store.

React Hook Form has built-in support for nested forms; can register a master form across steps.

## Step navigation

Two patterns:

- **Linear:** 1 → 2 → 3 → 4. User cannot skip. Keep the step order in one array and index into it, so next and back are just index math.

- **Branching:** path depends on earlier answers. "Are you a business?" yes → business steps; no → personal steps. Drive the branch off watched field values so the path recomputes when an earlier answer changes.

## Validation per step

Each step has its own validation. Strategies:

- Validate on Next button click — trigger validation for the current step's fields only; if any fail, block advancement and move focus to the first error.

- Allow free movement; flag invalid steps — let users jump around but mark incomplete steps in the indicator; good for edit-heavy flows like settings where people revisit.

- Async validation (server check) before allowing Next — for checks like username availability or coupon validity, show a pending state on Next and handle the request failing without trapping the user.

RHF + Zod provides good ergonomics. Each step defines its schema; combined schema validates the whole form on submit.

## The "Back" question

Should clicking Back preserve user input on the current step? Almost always yes — frustrating otherwise.

If the form mutates server state at each step (rare): Back reverts the server state too, or stays at current step.

## Step indicator

Common patterns:

- Numbered dots ("1 of 5") — best for short, fixed flows; each dot can show done, current, and upcoming states.

- Progress bar (50% complete) — works when the step count is large or variable and individual step names don't matter much.

- Step labels with active highlight ("Personal Info > Address > Payment") — names each step so users anticipate what's coming; make the labels clickable only when free navigation is allowed.

Show progress; reduce abandonment.

## Persistence

If users abandon mid-wizard:

- Save to localStorage on each Next — serialize the whole form object under one key, and write on step change rather than every keystroke to avoid thrash.

- On return, restore state and offer "continue where you left off" — detect saved state on mount and prompt, rather than silently rehydrating and surprising the user with stale data.

- Clear after successful submission or after expiration window — delete the draft on submit and store a timestamp so week-old, likely-stale data isn't restored.

For high-value flows (checkout): persist to server too.

## Submission

Final step submits the entire form. Patterns:

- Disable submit during in-flight request — track a submitting flag, not just a spinner, so a double click can't fire two submissions.

- Show loading state — give the button a spinner or "Submitting…" label so the user knows the click registered.

- On success: redirect or show confirmation — then clear the persisted draft so a back-navigation can't resubmit.

- On failure: surface error, jump back to relevant step if needed — map server field errors back to their step and navigate there so the user fixes the exact field.

## Accessibility

- Each step has its own form, heading, and field labels — give every step a real heading and associate every input with a label, since screen-reader users navigate by heading.

- aria-current on the current step in the indicator — set aria-current="step" so assistive tech announces the user's position in the sequence.

- Focus management: when advancing, focus moves to first field of new step — otherwise keyboard users are stranded on the Next button after each transition.

- Errors announced via aria-live — put the error summary in an aria-live="polite" region so new errors are read aloud without stealing focus.

## Mobile considerations

- One screen per step (vertical layout) — single column, one task per screen; avoid side-by-side fields that force horizontal scrolling on phones.

- Sticky bottom buttons (Back / Next) — pin them so the user never has to scroll to find how to advance.

- Keyboard avoidance for inputs — make the focused field scroll above the on-screen keyboard instead of hiding behind it.

- Numeric keyboard for number fields — set inputmode="numeric" (and type="tel" for codes) so phones show the number pad rather than the full keyboard.

## Step transitions

Subtle slide animation between steps gives a feeling of progress. Don't over-animate; 200–300ms is plenty.

## Common antipatterns

- Lose data on back navigation — keep all step data in one store so unmounting a step component never discards its values.

- Validation only on submit (user discovers all errors at end) — someone fills five steps, then gets sent back to step one; validate per step instead.

- Required fields not labeled clearly — mark required fields visibly, not by color alone, and state the rule before the user submits.

- No progress indicator — without a sense of length users abandon; even a plain "Step 2 of 4" helps.

- Confirmation modal blocking the user from changing their mind — don't trap people in a confirm dialog with no easy path back to edit their answers.

## Frequently Asked Questions

### How many steps is too many?

5–7 is the sweet spot. Above 10, users abandon. Combine logically related fields per step.

### Should the back button preserve the URL state?

For long flows: yes — use URL query params for the current step. Browser back works as expected.

### How do I handle wizards with conditional steps?

Maintain a step graph; compute next/previous based on current state. React Hook Form's field watchers help.
