Custom Shadcn Stepper for React and Tailwind CSS. A step-by-step process for users to navigate through a series of steps.
import {
Browse 15 production-ready Shadcn Stepper components for dashboards, forms, and product UI. These examples use Base UI primitives from @base-ui/react and stay fully compatible with Shadcn Create so radius, color, and typography match your configured theme.
Browse all 15 Shadcn Stepper components for copy-ready layouts, dashboards, and forms built with Tailwind CSS in the ReUI library.
<Stepper defaultValue={1}>
<StepperNav>
<StepperItem step={1}>
<StepperTrigger>
<StepperIndicator>1</StepperIndicator>
</StepperTrigger>
<StepperSeparator />
</StepperItem>
<StepperItem step={2}>
<StepperTrigger>
<StepperIndicator>2</StepperIndicator>
</StepperTrigger>
</StepperItem>
</StepperNav>
<StepperPanel>
<StepperContent value={1}>Content 1</StepperContent>
<StepperContent value={2}>Content 2</StepperContent>
</StepperPanel>
</Stepper>The root component that manages the active step and configuration.
A container for the stepper items, usually displayed as a progress bar or navigation trail.
Represents a single step in the process.
The interactive element used to navigate between steps.
Displays the status of the step (e.g., number, checkmark, or custom icon).
A visual line between steps.
The label for the step.
Additional supporting text for the step.
A container for step content panels.
The actual content associated with a specific step.
type StepIndicators = {
active?: React.ReactNode
completed?: React.ReactNode
inactive?: React.ReactNode
loading?: React.ReactNode
}"use client"
import { useState } from "react"
import { Badge } from "@/components/reui/badge"
import {
Stepper,
StepperContent,
StepperIndicator,
StepperItem,
StepperNav,
StepperPanel,
StepperSeparator,
StepperTitle,
StepperTrigger,
} from "@/components/reui/stepper"
import { Button } from "@/components/ui/button"
import { BookUserIcon, CheckIcon, CreditCardIcon, LoaderCircleIcon, LockIcon } from 'lucide-react'
const steps = [
{
title: "User Details",
icon: (
<BookUserIcon className="size-4" />
),
},
{
title: "Payment Info",
icon: (
<CreditCardIcon className="size-4" />
),
},
{
title: "Auth OTP",
icon: (
<LockIcon className="size-4" />
),
},
]
export function Pattern() {
const [currentStep, setCurrentStep] = useState(2)
return (
<Stepper
value={currentStep}
onValueChange={setCurrentStep}
indicators={{
completed: (
<CheckIcon className="size-3.5" />
),
loading: (
<LoaderCircleIcon className="size-3.5 animate-spin" />
),
}}
className="w-full max-w-xl space-y-8"
>
<StepperNav className="gap-3">
{steps.map((step, index) => (
<StepperItem
key={index}
step={index + 1}
className="relative flex-1 items-start"
>
<StepperTrigger
className="flex grow flex-col items-start justify-center gap-2.5"
asChild
>
<StepperIndicator className="data-[state=inactive]:border-border data-[state=inactive]:text-muted-foreground data-[state=completed]:bg-success size-8 border-2 data-[state=completed]:text-white data-[state=inactive]:bg-transparent">
{step.icon}
</StepperIndicator>
<div className="flex flex-col items-start gap-1">
<div className="text-muted-foreground text-[10px] font-semibold uppercase">
Step {index + 1}
</div>
<StepperTitle className="group-data-[state=inactive]/step:text-muted-foreground text-start text-base font-semibold">
{step.title}
</StepperTitle>
<div>
<Badge
size="sm"
variant="primary-light"
className="hidden group-data-[state=active]/step:inline-flex"
>
In Progress
</Badge>
<Badge
variant="success-light"
size="sm"
className="hidden group-data-[state=completed]/step:inline-flex"
>
Completed
</Badge>
<Badge
variant="secondary"
size="sm"
className="text-muted-foreground hidden group-data-[state=inactive]/step:inline-flex"
>
Pending
</Badge>
</div>
</div>
</StepperTrigger>
{steps.length > index + 1 && (
<StepperSeparator className="group-data-[state=completed]/step:bg-success absolute inset-x-0 start-9 top-4 m-0 group-data-[orientation=horizontal]/stepper-nav:w-[calc(100%-2rem)] group-data-[orientation=horizontal]/stepper-nav:flex-none" />
)}
</StepperItem>
))}
</StepperNav>
<StepperPanel className="text-sm">
{steps.map((step, index) => (
<StepperContent
key={index}
value={index + 1}
className="flex items-center justify-center"
>
{step.title} content
</StepperContent>
))}
</StepperPanel>
<div className="flex items-center justify-between gap-2.5">
<Button
variant="outline"
onClick={() => setCurrentStep((prev) => prev - 1)}
disabled={currentStep === 1}
>
Previous
</Button>
<Button
variant="outline"
onClick={() => setCurrentStep((prev) => prev + 1)}
disabled={currentStep === steps.length}
>
Next
</Button>
</div>
</Stepper>
)
}