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#

# One-time setup (tokens + lib helpers)npx koalaui-cli@latest init# Add this component (its dependencies come along)npx koalaui-cli@latest add stepper

Usage#

usage.tsxTSX
"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.

PropTypeDefaultDescription
valuenumber-The active step (1-based), controlled. Pair with onValueChange.
defaultValuenumber1The 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 attributeDescription
data-orientationhorizontal or vertical.
CSS variableDescription
--step-sizeThe 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.

PropTypeDefaultDescription
step*number-This step's 1-based position.
completedboolean-Forces the completed state regardless of the active step, for non-linear flows.
disabledbooleanfalseBlocks selection through the trigger and mutes the step.
loadingbooleanfalseSwaps the indicator glyph for a Spinner.
Data attributeDescription
data-statecompleted (before the active step), active or inactive.
data-disabledPresent when disabled.
data-loadingPresent 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.

PropTypeDefaultDescription
asChildbooleanfalseRenders onto the child element via Radix Slot instead of a <button>.
staticbooleanfalseKept 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.

FAQ#

Only if you want navigation. The Stepper is controllable: pass `value` and `onValueChange` to drive it from your Back and Next buttons, or use `defaultValue` for a static read-only progress indicator. Step numbers are 1-based.