shadcn.io is not affiliated with official shadcn/ui
Shadcn Steps for React and Tailwind
Steps in a wizard-like format.
Step 1
Step 2
Step 3
All steps completed. You're all set!
Installation
bunx --bun shadcn@latest add https://kit.dev/r/steps.jsonpnpm dlx shadcn@latest add https://kit.dev/r/steps.jsonnpx shadcn@latest add https://kit.dev/r/steps.jsonyarn shadcn@latest add https://kit.dev/r/steps.jsonInstall the following dependencies:
bun add @ark-ui/react lucide-reactpnpm add @ark-ui/react lucide-reactnpm install @ark-ui/react lucide-reactyarn add @ark-ui/react lucide-reactCopy and paste the following code into your project.
"use client";
import { ark } from "@ark-ui/react/factory";
import { Steps as ArkSteps, useStepsContext } from "@ark-ui/react/steps";
import { CheckIcon } from "lucide-react";
import type React from "react";
import { cn } from "@/lib/utils";
export const useSteps = useStepsContext;
export const Steps = (props: React.ComponentProps<typeof ArkSteps.Root>) => {
const { className, ...rest } = props;
return (
<ArkSteps.Root
className={cn(
"flex flex-col gap-4",
"data-[orientation=vertical]:min-h-32 data-[orientation=vertical]:flex-row data-[orientation=vertical]:gap-8",
className
)}
data-slot="steps"
{...rest}
/>
);
};
export const StepsList = (
props: React.ComponentProps<typeof ArkSteps.List>
) => {
const { className, ...rest } = props;
return (
<ArkSteps.List
className={cn(
"[--steps-gutter:--spacing(2)] [--steps-icon-size:--spacing(4)] [--steps-size:--spacing(8)]",
"flex",
"data-[orientation=vertical]:flex-col data-[orientation=vertical]:items-start",
"data-[orientation=horizontal]:items-center data-[orientation=horizontal]:justify-between",
className
)}
data-slot="steps-list"
{...rest}
/>
);
};
export const StepsItem = (
props: React.ComponentProps<typeof ArkSteps.Item>
) => {
const { className, ...rest } = props;
return (
<ArkSteps.Item
className={cn(
"group/step",
"relative flex flex-1",
"data-[orientation=vertical]:items-start",
"data-[orientation=horizontal]:items-center",
"last:flex-initial last:**:data-[slot=steps-separator]:hidden",
className
)}
data-slot="steps-item"
{...rest}
/>
);
};
interface StepsTriggerProps
extends React.ComponentProps<typeof ArkSteps.Trigger> {}
export const StepsTrigger = (props: StepsTriggerProps) => {
const { className, ...rest } = props;
return (
<ArkSteps.Trigger
className={cn(
"inline-flex items-center gap-3",
"cursor-pointer rounded-full outline-none",
"disabled:pointer-events-none disabled:opacity-64",
className
)}
data-slot="steps-trigger"
{...rest}
/>
);
};
export const StepsIndicator = (
props: React.ComponentProps<typeof ArkSteps.Indicator>
) => {
const { className, children, ...rest } = props;
return (
<ArkSteps.Indicator
className={cn(
"flex shrink-0 items-center justify-center tabular-nums",
"size-(--steps-size)",
"bg-muted text-muted-foreground",
"font-medium text-sm",
"rounded-full border",
"transition-colors",
"in-focus-visible:ring-[3px] in-focus-visible:ring-ring/32",
"data-current:border-primary data-current:bg-primary data-current:text-primary-foreground",
"data-complete:border-primary data-complete:bg-primary data-complete:text-primary-foreground",
"[&_svg]:size-(--steps-icon-size) [&_svg]:shrink-0",
"motion-reduce:transition-none!",
className
)}
data-slot="steps-indicator"
{...rest}
>
<span className="group-data-complete/step:hidden">{children}</span>
<CheckIcon className="hidden group-data-complete/step:block" />
</ArkSteps.Indicator>
);
};
export const StepsSeparator = (
props: React.ComponentProps<typeof ArkSteps.Separator>
) => {
const { className, ...rest } = props;
return (
<ArkSteps.Separator
className={cn(
"flex-1",
"bg-border",
"rounded-full",
"transition-colors",
"data-complete:bg-primary",
"data-[orientation=horizontal]:mx-(--steps-gutter) data-[orientation=horizontal]:h-0.5 data-[orientation=horizontal]:w-full",
"data-[orientation=vertical]:absolute data-[orientation=vertical]:top-[calc(var(--steps-size)+var(--steps-gutter))] data-[orientation=vertical]:left-[calc(var(--steps-size)/2-1px)] data-[orientation=vertical]:h-full data-[orientation=vertical]:max-h-[calc(100%-(var(--steps-size)+var(--steps-gutter)*2))] data-[orientation=vertical]:w-0.5",
"motion-reduce:transition-none!",
className
)}
data-slot="steps-separator"
{...rest}
/>
);
};
export const StepsTitle = (props: React.ComponentProps<typeof ark.span>) => {
const { className, ...rest } = props;
return (
<ark.span
className={cn("font-medium text-sm leading-none", className)}
data-slot="steps-title"
{...rest}
/>
);
};
export const StepsDescription = (
props: React.ComponentProps<typeof ark.span>
) => {
const { className, ...rest } = props;
return (
<ark.span
className={cn("text-muted-foreground text-xs", className)}
data-slot="steps-description"
{...rest}
/>
);
};
export const StepsContent = (
props: React.ComponentProps<typeof ArkSteps.Content>
) => {
const { className, ...rest } = props;
return (
<ArkSteps.Content
className={cn("data-[orientation=vertical]:flex-1", className)}
data-slot="steps-content"
{...rest}
/>
);
};
export const StepsCompletedContent = (
props: React.ComponentProps<typeof ArkSteps.CompletedContent>
) => {
const { className, ...rest } = props;
return (
<ArkSteps.CompletedContent
className={cn("data-[orientation=vertical]:flex-1", className)}
data-slot="steps-completed-content"
{...rest}
/>
);
};
export const StepsPrevious = (
props: React.ComponentProps<typeof ArkSteps.PrevTrigger>
) => <ArkSteps.PrevTrigger data-slot="steps-previous" {...props} />;
export const StepsNext = (
props: React.ComponentProps<typeof ArkSteps.NextTrigger>
) => <ArkSteps.NextTrigger data-slot="steps-next" {...props} />;Update the import paths to match your project setup.
Anatomy
Steps
├── StepsList
├── StepsItem
├── StepsTrigger
├── StepsIndicator
├── StepsSeparator
├── StepsContent
├── StepsNext
├── StepsPrevious
└── StepsProgressUsage
import { ChevronLeftIcon, ChevronRight } from "lucide-react";
import { Button } from "@/components/ui/button";
import {
Steps,
StepsCompletedContent,
StepsContent,
StepsIndicator,
StepsItem,
StepsList,
StepsNext,
StepsPrevious,
StepsSeparator,
StepsTrigger,
} from "@/components/ui/steps";const steps = Array.from({ length: 3 }, (_, index) => index + 1);
const StepsDemo = () => (
<Steps className="size-full max-w-md" count={steps.length}>
<StepsList>
{steps.map((step) => (
<StepsItem index={step - 1} key={step}>
<StepsTrigger>
<StepsIndicator>{step}</StepsIndicator>
</StepsTrigger>
<StepsSeparator />
</StepsItem>
))}
</StepsList>
{steps.map((step) => (
<StepsContent
className="flex h-full items-center justify-center rounded-md border"
index={step - 1}
key={step}
>
<p className="text-muted-foreground text-sm">Step {step}</p>
</StepsContent>
))}
<StepsCompletedContent className="flex h-full items-center justify-center rounded-md border">
<p className="text-muted-foreground text-sm">
All steps completed. You're all set!
</p>
</StepsCompletedContent>
<div className="flex flex-row-reverse gap-2">
<StepsNext asChild>
<Button>
Next
<ChevronRight />
</Button>
</StepsNext>
<StepsPrevious asChild>
<Button variant="outline">
<ChevronLeftIcon />
Back
</Button>
</StepsPrevious>
</div>
</Steps>
);Accessibility
| Key | Description |
|---|---|
Enter | Activate the focused control. |
Space | Activate the focused control. |
Demos
Controlled
Description
Icon
Loading
Title
Vertical
API Reference
Steps
div
PropType
AttributeType
CSS variableType
StepsList
div
PropType
AttributeType
StepsItem
div
PropType
AttributeType
StepsTrigger
button
PropType
AttributeType
StepsIndicator
div
PropType
AttributeType
CSS variableType
StepsSeparator
div
PropType
AttributeType
CSS variableType
StepsContent
div
PropType
AttributeType
StepsNext
button
PropType
AttributeType
StepsPrevious
button
PropType
AttributeType
StepsTitle
span
PropType
AttributeType
StepsDescription
span
PropType
AttributeType
StepsCompletedContent
div
PropType
AttributeType
useSteps
PropType