Stepper
Show where a person is in a multi-step flow and what is done.
motionproduct
Preview
Stepper preview
Live · keyboard ready
When to use
- Showing position in a strict multi-step flow like checkout or account setup.
- Flows where people can jump back to completed steps, via onStepSelect.
- Vertical timelines of steps with descriptions and error states.
When not to use
- Use multi-step-form when you also want the step content, navigation, and success state.
- Use onboarding-checklist for loosely ordered tasks.
- Use progress when a single percentage is enough.
shadcn CLI
Also installs (1)
- manicat-foundation
Manual install(+)
registry.jsonjson
{
"$schema": "https://ui.shadcn.com/schema/registry-item.json",
"name": "stepper",
"type": "registry:ui",
"title": "Stepper",
"description": "Show where a person is in a multi-step flow and what is done.",
"categories": [
"feedback"
],
"dependencies": [
"motion"
],
"registryDependencies": [
"https://manicat.viraza.net/r/manicat-foundation.json"
],
"files": [
{
"path": "registry/components/stepper/stepper.tsx",
"type": "registry:component",
"target": "@components/manicat/stepper/stepper.tsx"
},
{
"path": "registry/components/stepper/stepper.module.css",
"type": "registry:component",
"target": "@components/manicat/stepper/stepper.module.css"
}
],
"docs": "Docs and live preview: https://uiarc.dev/components/stepper",
"meta": {
"tier": "free",
"kind": "component",
"docs": "https://manicat.viraza.net/components/stepper",
"markdown": "https://uiarc.dev/components/stepper/markdown",
"tags": [
"motion",
"product"
]
}
}Usage
Install the component, then copy this into your app. Change the content and the props to fit your screen.
example.tsxtsx
"use client";
import { useState } from "react";
import { Stepper } from "@/components/manicat/stepper/stepper";
import { Button } from "@/components/manicat/button/button";
const steps = [
{ id: "plan", label: "Plan", description: "Choose a template and a plan." },
{ id: "connect", label: "Connect", description: "Link your GitHub repository." },
{ id: "deploy", label: "Deploy", description: "Ship the first version." },
];
export function StepperExample() {
const [current, setCurrent] = useState(1);
return (
<div className="grid w-full max-w-2xl gap-4">
<Stepper steps={steps} current={current} onStepSelect={setCurrent} details="all" />
<div className="flex gap-2">
<Button size="sm" variant="secondary" onClick={() => setCurrent((value) => Math.max(0, value - 1))}>
Back
</Button>
<Button size="sm" onClick={() => setCurrent((value) => Math.min(steps.length, value + 1))}>
Next
</Button>
</div>
</div>
);
}
Examples
Real screens rather than isolated props. Switch to Code to copy a whole example into your project.
Onboarding flow A step indicator driven by Back and Next, with completed steps clickable.
Onboarding flow
A step indicator driven by Back and Next, with completed steps clickable. preview
Live · keyboard ready
onboarding-flow-a-step-indicator-driven-by-back-and-next-with-completed-steps-clickable-.tsxtsx
"use client";
import { useState } from "react";
import { Button } from "@/components/manicat/button/button";
import { Stepper } from "@/components/manicat/stepper/stepper";
const onboarding = [
{ id: "plan", label: "Plan", description: "Choose a template and a plan." },
{ id: "connect", label: "Connect", description: "Link your GitHub repository." },
{ id: "deploy", label: "Deploy", description: "Ship the first version." },
];
export function StepperOnboardingExample() {
const [current, setCurrent] = useState(1);
return (
<div className="grid w-full max-w-2xl gap-4">
<Stepper steps={onboarding} current={current} onStepSelect={setCurrent} details="all" />
<div className="flex gap-2">
<Button size="sm" variant="secondary" onClick={() => setCurrent((value) => Math.max(0, value - 1))}>
Back
</Button>
<Button size="sm" onClick={() => setCurrent((value) => Math.min(onboarding.length, value + 1))}>
Next
</Button>
</div>
</div>
);
}
Keyboard interactions
| Keys | Action |
|---|---|
| ArrowLeft / ArrowRight / ArrowUp / ArrowDown | With onStepSelect, moves focus between reachable steps (RTL aware). |
| Home / End | Jumps to the first or the current step. |
| Enter / Space | Selects a completed step. |
Accessibility
- Renders a labelled nav when interactive, otherwise role="group"; the current step has aria-current="step".
- Each step appends screen-reader-only status text: Completed, Not started, or Error.
- Step changes are announced in a polite live region as "Step 2 of 4: Shipping".
- Upcoming steps are aria-disabled and removed from the tab order.
Motion
- Connectors fill one after another when progress jumps several steps; the ring then grows around the new current marker.
- Numbers morph into drawn checks or alerts; descriptions rise in while their slot springs to the new height.
- Reduced motion applies all changes instantly.
Responsive behavior
- Horizontal steppers switch to markers only with a caption below a 30rem container width.
- Set orientation to vertical for sidebars or narrow columns where labels and descriptions should stay visible.
- Labels and descriptions wrap with overflow-wrap, so long words do not overflow.
Performance
- The stepper only indicates; it renders markers and text, with a ResizeObserver for the description slot height.
Notes for AI
- Use to show position in a strict multi-step flow such as checkout or setup. Use onboarding-checklist for loosely ordered tasks.
- The stepper only indicates; render the step content yourself and drive current. Set current to steps.length when done.