Stepper
A progress indicator for sequential flows: onboarding, checkout and multi-step dialogs. Each step derives its state from a single active value, and the connecting line fills as you advance.
Installation#
Usage#
"use client"
import * as React from "react"
import {
Stepper,
StepperItem,
StepperTrigger,
StepperIndicator,
StepperTitle,
StepperSeparator,
} from "@/components/ui/stepper"
export function Example() {
const [step, setStep] = React.useState(1)
return (
<Stepper value={step} onValueChange={setStep}>
<StepperItem step={1}>
<StepperTrigger>
<StepperIndicator />
<StepperTitle>Account</StepperTitle>
</StepperTrigger>
<StepperSeparator />
</StepperItem>
<StepperItem step={2}>
<StepperTrigger>
<StepperIndicator />
<StepperTitle>Confirm</StepperTitle>
</StepperTrigger>
</StepperItem>
</Stepper>
)
}Anatomy#
import {
Stepper,
StepperItem,
StepperTrigger,
StepperIndicator,
StepperTitle,
StepperDescription,
StepperSeparator,
} from "@/components/ui/stepper"
<Stepper>
<StepperItem step={1}>
<StepperTrigger>
<StepperIndicator />
<StepperTitle />
<StepperDescription />
</StepperTrigger>
<StepperSeparator />
</StepperItem>
</Stepper>Orientation#
Orientation is set on the root. horizontal lays steps in a row with a flexible connector; vertical stacks them with the connector running down the indicator column.
With descriptions#
Pair each StepperTitle with a StepperDescription in a vertical stack. Vertical orientation gives the supporting copy room to breathe.
Density#
Density retunes the indicator size, connector geometry, and text, without touching color or radius. comfortable suits onboarding; compact fits dense app UI and dialogs.
Loading & disabled#
Set loading on the active step to swap its number for a spinner while it validates or submits. Mark unreachable steps disabled to block selection and mute them.
In a dialog#
The headline use case. Reach for DialogStepper (from @/components/ui/dialog). It places the stepper full-bleed with a divider beneath and inherits the dialog's density. Drive its value from the dialog's own step state and swap the body per step; future steps are disabled so the user can't skip ahead.
API reference#
Stepper
The root: holds the active step and renders a <div role="group">. Forwards all native <div> props except onChange; every part accepts className, merged last.
| Prop | Type | Default | Description |
|---|---|---|---|
| value | number | - | The active step (1-based), controlled. Pair with onValueChange. |
| defaultValue | number | 1 | The initial active step (1-based), uncontrolled. |
| onValueChange | (step: number) => void | - | Fires when a StepperTrigger selects its step. |
| variant | "outline" | "solid" | "outline" | outline is a ringed circle with a halo on the active step; solid fills every state flat. |
| orientation | "horizontal" | "vertical" | "horizontal" | A row with the label under each circle, or a column with the label beside it. |
| density | "comfortable" | "compact" | - | Indicator size (28 or 24px) and text tier. Inherits the nearest DensityProvider when omitted. |
| Data attribute | Description |
|---|---|
| data-orientation | horizontal or vertical. |
| CSS variable | Description |
|---|---|
| --step-size | The indicator diameter, set per density (1.75rem or 1.5rem); every connector offset derives from it. |
StepperItem
One step. Derives its state from the root's active value and its own step. Forwards all native <div> props.
| Prop | Type | Default | Description |
|---|---|---|---|
| step* | number | - | This step's 1-based position. |
| completed | boolean | - | Forces the completed state regardless of the active step, for non-linear flows. |
| disabled | boolean | false | Blocks selection through the trigger and mutes the step. |
| loading | boolean | false | Swaps the indicator glyph for a Spinner. |
| Data attribute | Description |
|---|---|
| data-state | completed (before the active step), active or inactive. |
| data-disabled | Present when disabled. |
| data-loading | Present when loading. |
StepperTrigger
The clickable region that selects its step; omit it for a read-only progress indicator. Renders a <button> and forwards all its props; sets aria-current="step" on the active step.
| Prop | Type | Default | Description |
|---|---|---|---|
| asChild | boolean | false | Renders onto the child element via Radix Slot instead of a <button>. |
| static | boolean | false | Kept for compatibility; controls no longer scale on press. |
StepperIndicator
The numbered circle: the step number (or children as a custom resting glyph), a check once completed, a spinner while loading. Forwards all native <span> props.
StepperTitle
The step's label, which turns to full ink once active or completed. Forwards all native <span> props.
StepperDescription
A muted secondary line under the title. Forwards all native <span> props.
StepperSeparator
The connector to the next step, placed last in every item but the last; its fill grows when the step completes. Hidden from assistive tech. Forwards all native <div> props.