Skip to content

Pro pre-sale is open. Installation opens January 2027. See Pro

Components

Stepper

Shows the steps of a flow in order and where the user is (checkout, onboarding, setup). Completed steps are buttons that go back to that step; upcoming steps cannot be reached from here. Use Tabs for switching views, not for a sequence.

  • npm: 1 direct, 2 total
  • registry: 2 direct, 2 total

Preview

  1. Shipping, current step
  2. Payment, not reached
  3. Review, not reached

Props

  • orientation
  • size
  • icons
  • descriptions
import { Stepper } from "@/components/ui/stepper"

const checkoutSteps = [
  { id: "cart", label: "Cart" },
  { id: "shipping", label: "Shipping" },
  { id: "payment", label: "Payment" },
  { id: "review", label: "Review" },
]
const goToStep = (index: number) => { /* Show that step of the flow. */ }

<Stepper steps={checkoutSteps} current={1} onStepSelect={goToStep} />

Usage

When to use

  • Show where someone is in an ordered flow such as checkout, onboarding or setup.
  • Let someone return to a completed step without opening upcoming steps early.

When not to use

  • Switch between views of the same content. Use Tabs.
  • Show how much work is complete without naming sequential steps. Use Progress.
  • Navigate between pages or sections. Use Sidebar or BottomNav.

Variants

orientation

Row or column. In a column each label sits beside its indicator and the connector runs down to the next one. Use vertical below md when labels do not fit, or in a side column.

    1. Shipping, current step
    2. Payment, not reached
    3. Review, not reached

    horizontaldefault

    1. Shipping, current step
    2. Payment, not reached
    3. Review, not reached

    vertical

import { Stepper } from "@/components/ui/stepper"

const checkoutSteps = [
  { id: "cart", label: "Cart" },
  { id: "shipping", label: "Shipping" },
  { id: "payment", label: "Payment" },
  { id: "review", label: "Review" },
]
const goToStep = (index: number) => { /* Show that step of the flow. */ }

<Stepper steps={checkoutSteps} current={1} onStepSelect={goToStep} />
<Stepper orientation="vertical" steps={checkoutSteps} current={1} onStepSelect={goToStep} />

size

Indicator 24, 32 or 40px with 12, 16 or 20px icons; label font-size-sm, -md or -lg. sm for side columns and panels, lg for a roomy onboarding page. One size per region.

    1. Shipping, current step
    2. Payment, not reached
    3. Review, not reached

    sm

    1. Shipping, current step
    2. Payment, not reached
    3. Review, not reached

    mddefault

    1. Shipping, current step
    2. Payment, not reached
    3. Review, not reached

    lg

import { Stepper } from "@/components/ui/stepper"

const checkoutSteps = [
  { id: "cart", label: "Cart" },
  { id: "shipping", label: "Shipping" },
  { id: "payment", label: "Payment" },
  { id: "review", label: "Review" },
]
const goToStep = (index: number) => { /* Show that step of the flow. */ }

<Stepper size="sm" steps={checkoutSteps} current={1} onStepSelect={goToStep} />
<Stepper steps={checkoutSteps} current={1} onStepSelect={goToStep} />
<Stepper size="lg" steps={checkoutSteps} current={1} onStepSelect={goToStep} />

Examples

Checkout

  1. Shipping, current step
  2. Payment, not reached
  3. Review, not reached
import { Stepper } from "@/components/ui/stepper"

const checkoutSteps = [
  { id: "cart", label: "Cart" },
  { id: "shipping", label: "Shipping" },
  { id: "payment", label: "Payment" },
  { id: "review", label: "Review" },
]
const goToStep = (index: number) => { /* Show that step of the flow. */ }

<Stepper current={1} steps={checkoutSteps} onStepSelect={goToStep} />

Vertical with descriptions

Each label sits on its indicator's center line, with the description under it.

  1. Team, current stepInvite people
  2. Workspace, not reachedName and address
import { Stepper } from "@/components/ui/stepper"

const setupSteps = [
  { id: "account", label: "Account", description: "Email and password" },
  { id: "team", label: "Team", description: "Invite people" },
  { id: "workspace", label: "Workspace", description: "Name and address" },
]
const goToStep = (index: number) => { /* Show that step of the flow. */ }

<Stepper orientation="vertical" current={1} steps={setupSteps} onStepSelect={goToStep} />

With icons

A step's icon replaces its number until the step is complete.

  1. Payment, current step
  2. Review, not reached
import { IconCreditCard, IconReceipt, IconShoppingCart, IconTruck } from "@tabler/icons-react"
import { Stepper } from "@/components/ui/stepper"

const iconSteps = [
  { id: "cart", label: "Cart", icon: <IconShoppingCart size={16} /> },
  { id: "shipping", label: "Shipping", icon: <IconTruck size={16} /> },
  { id: "payment", label: "Payment", icon: <IconCreditCard size={16} /> },
  { id: "review", label: "Review", icon: <IconReceipt size={16} /> },
]
const goToStep = (index: number) => { /* Show that step of the flow. */ }

<Stepper current={2} steps={iconSteps} onStepSelect={goToStep} />

Small, in a side column

  1. Team, current stepInvite people
  2. Workspace, not reachedName and address
import { Stepper } from "@/components/ui/stepper"

const setupSteps = [
  { id: "account", label: "Account", description: "Email and password" },
  { id: "team", label: "Team", description: "Invite people" },
  { id: "workspace", label: "Workspace", description: "Name and address" },
]
const goToStep = (index: number) => { /* Show that step of the flow. */ }

<Stepper orientation="vertical" size="sm" current={1} steps={setupSteps} onStepSelect={goToStep} />

Do and don't

Do

  • Keep labels to one or two words.
  • Use 3 to 7 steps; more needs grouping.
  • Give every step an icon, or none.
  • Show the step count in the page heading too (Step 2 of 4) when the Stepper is hidden on small screens.

Don't

  • Do not use Tabs for a sequence the user must follow in order.
  • Do not use as navigation to steps the user has not reached.
  • Do not mix two sizes of Stepper on one screen.

Props

NameTypeValuesDefaultDescription
steps required{ id: string; label: string; description?: string; status?: "error"; icon?: React.ReactNode }[]--The steps in order. icon replaces the number on a current or upcoming step; the indicator sets its size and color, and completed and error steps keep their check and warning icons. status error overrides the computed visual state of that step.
current requirednumber--Zero-based index of the current step. Steps before it are complete.
orientationenumhorizontal | verticalhorizontalRow or column. In a column each label sits beside its indicator and the connector runs down to the next one. Use vertical below md when labels do not fit, or in a side column.
sizeenumsm | md | lgmdIndicator 24, 32 or 40px with 12, 16 or 20px icons; label font-size-sm, -md or -lg. sm for side columns and panels, lg for a roomy onboarding page. One size per region.
onStepSelect(index: number) => void--Called with the index when a completed step is activated, to go back to it. Required by the type when current is greater than zero, because an earlier step can be activated.
statusLabels{ completed?: string; current?: string; upcoming?: string; error?: string; goBack?: string }--Words added to each step's accessible name, as {label}, {status}: { completed, current, upcoming, error }. Defaults: completed, current step, not reached, has an error. Vietnamese: đã xong, bước hiện tại, chưa đến, có lỗi. goBack names a completed step button: EN `{label}, completed, go back to this step`, VI `{label}, đã xong, quay lại bước này`.

Slots

NameRequiredDescription
stepIconnoOptional icon per step, replacing the number on a current or upcoming step (never the check or warning icon). Any SVG node; the indicator sets its size and color.

Anatomy

PartDescription
rootThe ol that lays out the steps in a row or a column.
stepOne li: its content (a button on a completed step) and its connectors. In a row, the indicator sits above the label; in a column, beside it, with space below for the connector.
indicatorThe filled circle, with no outline: the step number or the step's icon, a check when complete, a warning icon on error. 24, 32 or 40px by size.
stepIndicatorThe indicator inside a completed step's button. Hover and press darken the circle only, not a box around the label.
iconAny icon in the indicator (the step's own, the check, the warning): 12, 16 or 20px by size, colored for the step's state.
labelThe step title, medium weight on the current step. In a column, it sits on the indicator's center line.
descriptionOptional secondary line under the label.
connectorThe 2px line between two indicators, with a gap at each end; accent once the earlier step is complete.

Accessibility

Role: list

KeysAction
TabMoves to the next completed step (a button). The current and upcoming steps are not focusable.
Enter / SpaceGoes back to the focused completed step through onStepSelect.
  • An ordered list. The current step has aria-current="step" and sets data-current.
  • A completed step is a button named EN `{label}, completed, go back to this step`, VI `{label}, đã xong, quay lại bước này`.
  • Each step's accessible text includes its state word (completed, current, upcoming, error) from statusLabels, not only the icon or color.
  • The indicator is hidden from screen readers: the list gives the position and the label gives the state. The connector lines are decorative (aria-hidden).
  • Steps with an error show a warning icon on the soft danger fill and the word from statusLabels.error.
  • A completed step's button reaches size-target-min in height on touch, through a transparent ::before.

Install

npx werewolf-ui add stepper

Run npx werewolf-ui init once in your app before adding components. Init sets up tokens, their CSS import and agent rules. Add installs this component and its dependencies while preserving your theme. See Installation for requirements and the alternative registry setup.