Back to React
React
medium
mid

How would you manage navigation between steps and handle validation in a multi step form?

Track currentStep in state; gate 'Next' on validating the current step's slice. Keep per-step errors in shared state, allow free Back navigation, mark steps visited/valid, sync step to the URL for deep-linking and refresh, and block final submit until all steps are valid.

4 min read·~8 min to think through

The companion to "where does the data live" — this is how the user moves through the wizard and how validation gates that movement.

Navigation state

jsx
const [currentStep, setCurrentStep] = useState(0);
const [errors, setErrors] = useState({});      // keyed by step
const [visited, setVisited] = useState({ 0: true });

Validation gates "Next" — not "Back"

  • Going forward — validate the current step's slice before advancing. If it fails, set that step's errors and stay put.
  • Going back — always allowed freely; never make a user re-pass validation to look at a previous step.
jsx
const next = () => {
  const stepErrors = validateStep(currentStep, formData);
  if (Object.keys(stepErrors).length) {
    setErrors((e) => ({ ...e, [currentStep]: stepErrors }));
    return; // blocked
  }
  setVisited((v) => ({ ...v, [currentStep + 1]: true }));
  setCurrentStep((s) => s + 1);
};
const back = () => setCurrentStep((s) => Math.max(0, s - 1));

Error handling per step

  • Keep errors in shared state keyed by step (not local to a step component, which unmounts).
  • Show field-level errors inline; optionally a step-level summary.
  • A stepper/progress UI that marks each step as untouched / valid / has-errors, so the user can see where problems are.
  • Let users jump to a step via the stepper only if intermediate steps are valid (or only to visited steps).

The details that signal seniority

  • Sync currentStep to the URL (/wizard/step-2 or ?step=2) — makes the browser back button work intuitively, allows deep-linking, and survives refresh (paired with persisted data).
  • **Final submit is gated on all steps valid** — re-run every step's validation before submitting; jump the user to the first invalid step if something's wrong.
  • Re-validate on back-edits — if editing step 1 invalidates step 3, mark step 3 dirty.
  • Unsaved-changes guard — warn before navigating away from a partially-filled wizard.
  • Focus management & accessibility — move focus to the new step's heading on navigation; announce step changes.

The framing

"I track currentStep plus per-step errors and a visited map in the shared state. 'Next' is gated — validate the current step's slice, and only advance if it passes, otherwise surface that step's errors. 'Back' is always free. A stepper UI shows which steps are valid or have errors. The senior details: sync the step to the URL so the browser back button and refresh work, gate the final submit on all steps validating — jumping to the first invalid one — and handle focus and an unsaved-changes guard."

Follow-up questions

  • Why allow free Back navigation but gate Next?
  • Why sync the current step to the URL?
  • How do you handle a back-edit that invalidates a later step?
  • How do you prevent the final submit when an earlier step is invalid?

Common mistakes

  • Letting users advance past an invalid step.
  • Blocking Back navigation behind validation.
  • Keeping step errors in local step state that unmounts.
  • Not syncing step to the URL — broken back button and lost progress on refresh.
  • Not re-validating everything before final submit.

Performance considerations

  • Navigation is cheap; the cost is re-rendering. Memoize step components so only the active step renders, and keep validation per-slice rather than revalidating the whole form on every keystroke.

Edge cases

  • Editing an earlier step invalidates a later one.
  • User deep-links to step 3 without completing steps 1–2.
  • Browser back button vs. in-app navigation.
  • Refresh mid-wizard.

Real-world examples

  • Checkout wizards with a progress stepper and per-step validation.
  • Onboarding flows where the step is in the URL for resumability.

Senior engineer discussion

Seniors gate forward navigation on validation while keeping Back free, store errors in shared step-keyed state, sync the step to the URL, re-validate everything before submit, handle cross-step invalidation, and address focus management and unsaved-changes guards.

Related questions