Multi-Step Forms
Break long forms into steps so users see one chunk at a time, with progress, per-step validation, and optional draft persistence.
The problem I keep seeing
Checkout, onboarding, and long surveys often dump many fields on one page. Users get overwhelmed, abandon halfway, or hit submit only to see a wall of validation errors. You need a way to break the form into steps, validate each step before advancing, and optionally persist a draft so they can leave and come back.
Naive approach
One giant form with every field and a single submit. Simple to wire, but poor UX: no sense of progress, and validation fires for everything at once at the end.
First improvement
A wizard: keep a stepIndex (or step id) in state, render only the fields for the current step, and provide Next/Back. The user sees one logical “page” at a time and progress is clear.
Remaining issues
- Per-step validation: Validate only the current step before allowing “Next”; don’t run the full schema until the final submit.
- URL sync: Put the step in the URL (e.g.
?step=shipping) so the user can bookmark, share, or use the back button and land on the right step. - Draft: Persist form values (localStorage or API) so leaving the page doesn’t lose data; restore on return.
Production pattern
Use React Hook Form with FormProvider so all step components share the same form state. Derive the current step from useSearchParams so the URL is the source of truth. Before advancing, call trigger(fieldsForThisStep); only move to the next step if validation passes. Optionally persist a draft (e.g. debounced form.watch → localStorage or PATCH draft endpoint) and clear it on successful submit.
When I use this
- Checkout / long forms: 3–7 logical steps (details → shipping → payment) with clear progress and per-step validation.
- Onboarding: One or two questions per step to reduce drop-off.
- Skip when: Form has fewer than ~5–7 fields; a single page with good grouping and validation is enough.
Gotchas
- Step in URL: Normalize invalid or missing
stepto the first step so direct links don’t break. - Back button: If step is in the URL, browser back naturally goes to the previous step; don’t block it.
- Draft key: Use a key that includes user/session if multiple drafts exist (e.g.
'draft-' + userId).