Installation
Complete project setup first. Your configuration
must select react/radix and include your Design System ID.
pnpm exec frontfriend download stepper -f react --primitive radixReview dependencies reported by the downloader and keep your source changes in version control.
Usage
import {
Stepper,
StepperNav,
StepperItem,
StepperTrigger,
StepperIndicator,
StepperSeparator,
StepperPanel,
StepperContent,
} from "@/components/ui/stepper";
import * as React from "react";
export default function StepperExample() {
const [value, setValue] = React.useState(2);
return (
<Stepper
value={value}
onValueChange={setValue}
orientation="horizontal"
className="w-80"
>
<StepperNav>
{[1, 2, 3].map((step) => (
<StepperItem key={step} step={step}>
<StepperTrigger>
<StepperIndicator>{step}</StepperIndicator>
</StepperTrigger>
{step < 3 && <StepperSeparator />}
</StepperItem>
))}
</StepperNav>
<StepperPanel>
{[1, 2, 3].map((step) => (
<StepperContent key={step} value={step}>
Step {step}
</StepperContent>
))}
</StepperPanel>
</Stepper>
);
} Examples
Default
import {
Stepper,
StepperNav,
StepperItem,
StepperTrigger,
StepperIndicator,
StepperSeparator,
StepperPanel,
StepperContent,
} from "@/components/ui/stepper";
const steps = [1, 2, 3, 4];
<Stepper defaultValue={2} className="w-[420px] space-y-8">
<StepperNav>
{steps.map((step) => (
<StepperItem key={step} step={step}>
<StepperTrigger>
<StepperIndicator>{step}</StepperIndicator>
</StepperTrigger>
{steps.length > step && <StepperSeparator />}
</StepperItem>
))}
</StepperNav>
<StepperPanel className="text-sm">
{steps.map((step) => (
<StepperContent
key={step}
value={step}
className="flex items-center justify-center"
>
Step {step} content
</StepperContent>
))}
</StepperPanel>
</Stepper>; States
import {
Stepper,
StepperNav,
StepperItem,
StepperTrigger,
StepperIndicator,
StepperSeparator,
} from "@/components/ui/stepper";
import { Spinner } from "@/components/ui/spinner";
<Stepper
defaultValue={3}
indicators={{
loading: <Spinner size="3" className="animate-spin text-onbrand-mid" />,
}}
className="w-[420px] space-y-8"
>
<StepperNav>
<StepperItem step={1} completed>
<StepperTrigger>
<StepperIndicator>1</StepperIndicator>
</StepperTrigger>
<StepperSeparator />
</StepperItem>
<StepperItem step={2}>
<StepperTrigger>
<StepperIndicator>2</StepperIndicator>
</StepperTrigger>
<StepperSeparator />
</StepperItem>
<StepperItem step={3} loading>
<StepperTrigger>
<StepperIndicator>3</StepperIndicator>
</StepperTrigger>
<StepperSeparator />
</StepperItem>
<StepperItem step={4} disabled>
<StepperTrigger>
<StepperIndicator>4</StepperIndicator>
</StepperTrigger>
</StepperItem>
</StepperNav>
</Stepper>; Controlled
import {
Stepper,
StepperNav,
StepperItem,
StepperTrigger,
StepperIndicator,
StepperSeparator,
StepperPanel,
StepperContent,
} from "@/components/ui/stepper";
import { Button } from "@/components/ui/button";
import * as React from "react";
const steps = [1, 2, 3, 4];
export default function StepperExample() {
const [currentStep, setCurrentStep] = React.useState(2);
return (
<Stepper
value={currentStep}
onValueChange={setCurrentStep}
className="w-[420px] space-y-8"
>
<StepperNav>
{steps.map((step) => (
<StepperItem key={step} step={step}>
<StepperTrigger>
<StepperIndicator>{step}</StepperIndicator>
</StepperTrigger>
{steps.length > step && <StepperSeparator />}
</StepperItem>
))}
</StepperNav>
<StepperPanel className="text-sm">
{steps.map((step) => (
<StepperContent
key={step}
value={step}
className="flex items-center justify-center"
>
Step {step} content
</StepperContent>
))}
</StepperPanel>
<div className="flex items-center justify-between gap-2.5">
<Button
variant="tertiary"
size="sm"
onClick={() => setCurrentStep((step) => Math.max(1, step - 1))}
disabled={currentStep === 1}
>
Previous
</Button>
<Button
variant="tertiary"
size="sm"
onClick={() =>
setCurrentStep((step) => Math.min(steps.length, step + 1))
}
disabled={currentStep === steps.length}
>
Next
</Button>
</div>
</Stepper>
);
} Title & Description
import {
Stepper,
StepperNav,
StepperItem,
StepperTrigger,
StepperIndicator,
StepperTitle,
StepperDescription,
StepperSeparator,
} from "@/components/ui/stepper";
const titledSteps = [
{ title: "User Details", description: "Create your account" },
{ title: "Payment Info", description: "Add billing information" },
{ title: "Auth OTP", description: "Verify your identity" },
{ title: "Preview Form", description: "Review and finish" },
];
<Stepper defaultValue={2} className="w-[560px] space-y-8">
<StepperNav>
{titledSteps.map((step, index) => (
<StepperItem
key={step.title}
step={index + 1}
className="relative flex-1 items-start"
>
<StepperTrigger className="flex flex-col gap-2.5">
<StepperIndicator>{index + 1}</StepperIndicator>
<StepperTitle>{step.title}</StepperTitle>
<StepperDescription>{step.description}</StepperDescription>
</StepperTrigger>
{titledSteps.length > index + 1 && (
<StepperSeparator className="absolute left-[calc(50%+0.875rem)] right-[calc(-50%+0.875rem)] top-3 m-0 flex-none" />
)}
</StepperItem>
))}
</StepperNav>
</Stepper>; Vertical
import {
Stepper,
StepperNav,
StepperItem,
StepperTrigger,
StepperIndicator,
StepperTitle,
StepperDescription,
StepperSeparator,
} from "@/components/ui/stepper";
const titledSteps = [
{ title: "User Details", description: "Create your account" },
{ title: "Payment Info", description: "Add billing information" },
{ title: "Auth OTP", description: "Verify your identity" },
{ title: "Preview Form", description: "Review and finish" },
];
<Stepper
defaultValue={2}
orientation="vertical"
className="w-[420px] space-y-8"
>
<StepperNav>
{titledSteps.map((step, index) => (
<StepperItem
key={step.title}
step={index + 1}
className="relative items-start not-last:flex-1"
>
<StepperTrigger className="items-start gap-2.5 pb-12 last:pb-0">
<StepperIndicator>{index + 1}</StepperIndicator>
<div className="mt-0.5 grid gap-1 text-left">
<StepperTitle>{step.title}</StepperTitle>
<StepperDescription>{step.description}</StepperDescription>
</div>
</StepperTrigger>
{titledSteps.length > index + 1 && (
<StepperSeparator className="absolute inset-y-0 left-3 top-7 -order-1 m-0 -translate-x-1/2 group-data-[orientation=vertical]/stepper-nav:h-[calc(100%-2rem)]" />
)}
</StepperItem>
))}
</StepperNav>
</Stepper>; API reference
Frontfriend-owned props. Native HTML and inherited primitive props remain documented by Radix UI.
Stepper
| Prop | Type | Default | Description |
|---|---|---|---|
defaultValue | number | 1 | |
indicators | StepIndicators | {} | |
onValueChange | (value: number) => void | — | |
orientation | StepperOrientation | "horizontal" | |
value | number | — |
StepperContent
| Prop | Type | Default | Description |
|---|---|---|---|
forceMount | boolean | false | |
value | number | — |
StepperItem
| Prop | Type | Default | Description |
|---|---|---|---|
completed | boolean | false | |
disabled | boolean | false | |
loading | boolean | false | |
step | number | — |
StepperTrigger
| Prop | Type | Default | Description |
|---|---|---|---|
asChild | boolean | false |
Configuration determines the actual classes and available styling hooks. See Theming & component configuration before introducing a repeated local override.