Multi-step Form
A multi-step form container with direction-aware sliding transitions and a switchable progress indicator style.
This is a WebberUI Pro component
Free during the launch campaign: sign up or sign in, then hit “Copy install command” in the preview above and it installs straight away — no payment, no credit card. The command below returns 401 while you are signed out.
npx shadcn@latest add "https://webberui.com/r/multi-step-form.json?t=<install token>"Playground
Tune the props live — the code snippet updates as you go, so you can dial in the look you want before copying it.
<MultiStepForm />
Installation
npx shadcn@latest add "https://webberui.com/r/multi-step-form.json?t=<install token>"Or, once registries are configured in components.json, install it as @webberui/multi-step-form.
Usage
Declare each step with <MultiStepFormStep>; the container makes a direction-aware sliding transition based on the current step and renders the progress indicator and the back / next control bar automatically.
import {
MultiStepForm,
MultiStepFormStep,
} from "@/components/ui/multi-step-form";
<MultiStepForm variant="steps" onComplete={() => console.log("done")}>
<MultiStepFormStep label="Account">{/* ...fields */}</MultiStepFormStep>
<MultiStepFormStep label="Profile">{/* ...fields */}</MultiStepFormStep>
<MultiStepFormStep label="Confirm">{/* ...summary */}</MultiStepFormStep>
</MultiStepForm>;Custom control bar
Set controls to false and use the useMultiStepForm() hook to assemble your own navigation anywhere (so you can validate before calling next()):
import { useMultiStepForm } from "@/components/ui/multi-step-form";
function Footer() {
const { back, next, isFirst, isLast } = useMultiStepForm();
return (
<div>
<button onClick={back} disabled={isFirst}>
Back
</button>
<button onClick={next}>{isLast ? "Submit" : "Next"}</button>
</div>
);
}Controlled mode
Pass step and onStepChange to take full control of the current step — ideal for gating steps behind form validation.
const [step, setStep] = React.useState(0);
<MultiStepForm step={step} onStepChange={(next) => setStep(next)}>
{/* ...steps */}
</MultiStepForm>;Props
MultiStepForm
| Prop | Type | Default | Description |
|---|---|---|---|
children | ReactNode | — | The step declarations, all <MultiStepFormStep> |
step | number | — | Controlled current step index; when omitted, it is uncontrolled |
defaultStep | number | 0 | Initial step index in uncontrolled mode |
onStepChange | (step: number, direction: 1 | -1) => void | — | Fires when the step changes, with the new index and the direction |
onComplete | () => void | — | Fires when "Done" is pressed on the last step |
variant | "bar" | "dots" | "steps" | false | "steps" | Progress indicator style; false hides it |
controls | boolean | true | Whether to render the built-in control bar |
backLabel | string | "上一步" | Text of the back button |
nextLabel | string | "下一步" | Text of the next button |
completeLabel | string | "完成" | Text of the final-step button |
MultiStepFormStep
| Prop | Type | Default | Description |
|---|---|---|---|
label | string | — | Step title shown in the progress indicator |
children | ReactNode | — | Step content |
className | string | — | Appended to the step container's className |
useMultiStepForm()
Returns { step, total, direction, isFirst, isLast, goTo, next, back, reducedMotion } for use in a custom control bar or progress indicator.
How it works
- Three progress styles:
barprogress bar,dots(the current dot stretches into a pill), andstepsnumbered steps (completed ones get a checkmark and the connecting lines colour in with the progress). - Direction awareness: going forward, the new step slides in from the right and the old one exits to the left; going back is the reverse. The direction is derived during render from the previous index, and is correct in both controlled and uncontrolled modes.
- The viewport uses
overflow-hiddento clip the horizontal slide, andpopLayouttakes the exiting step out of flow so the entering step decides the container height, preventing the height from collapsing during the transition.
Accessibility
- Steps in the progress indicator that are current or already visited can be clicked to jump back (
goTo), while steps not yet reached are disabled, preserving the fill-in order. - The current step is marked
aria-current="step"; the progress bar providesrole="progressbar"andaria-valuenow. - Step changes are announced as "step x of y" through an
aria-live="polite"region. - The built-in control bar uses native
buttons that are keyboard-operable with afocus-visiblering; the back button is disabled on the first step. - When the user has "reduce motion" enabled at the system level, the slide degrades to a brief fade in / fade out, and the progress fill jumps straight into position.
File Dropzone
A drag-and-drop upload area — the magnetic dashed border leans toward the cursor and marches like a marquee, and the file list carries ring progress indicators.
Inline Edit Field
A click-to-edit inline field — the display and edit states swap seamlessly with a layout morph, Enter/blur submits and Esc cancels, with multiline support, controlled and uncontrolled modes, and no accidental submit mid-IME-composition.