Skip to main contentSkip to docs navigation

Step-by-step progress tracker for multi-stage workflows, with vertical and horizontal layouts and data-driven rendering via the items prop.

Overview

A stepper communicates progress through a sequence of discrete stages, rendered as an ordered list by default. Each step carries one of three implicit states — past, current, or next — derived entirely from which item carries active; there's no need to set a status on every other step individually.

Data-driven usage

Pass an items array to render steps from data. Mark the current step with active — items before it render as completed, items after it as upcoming.

  1. Account
  2. Shipping
  3. Payment
  4. Review
JSX
import { Stepper } from '@chassis-ui/react'

export const Example = () => {
  return (
    <Stepper
      items={[
        { label: 'Account' },
        { label: 'Shipping' },
        { label: 'Payment', active: true },
        { label: 'Review' }
      ]}
    />
  )
}

Manual markup

Build a stepper from <StepperItem> children for full control over each step's content.

  1. Account
  2. Shipping
  3. Payment
  4. Review
JSX
import { Stepper, StepperItem } from '@chassis-ui/react'

export const Example = () => {
  return (
    <Stepper>
      <StepperItem>Account</StepperItem>
      <StepperItem>Shipping</StepperItem>
      <StepperItem active>Payment</StepperItem>
      <StepperItem>Review</StepperItem>
    </Stepper>
  )
}

Complex content

Steps aren't limited to a single line of text — nest additional markup inside a <StepperItem> for descriptions, helper text, or other content alongside the label.

  1. Account
    Create a username and password.
  2. Shipping
    Where should the order ship?
  3. Payment
    Add a card or payment method.
JSX
import { Stepper, StepperItem } from '@chassis-ui/react'

export const Example = () => {
  return (
    <Stepper>
      <StepperItem className="align-items-start">
        <div>
          <div className="font-strong">Account</div>
          <div className="fg-subtle">Create a username and password.</div>
        </div>
      </StepperItem>
      <StepperItem active className="align-items-start">
        <div>
          <div className="font-strong">Shipping</div>
          <div className="fg-subtle">Where should the order ship?</div>
        </div>
      </StepperItem>
      <StepperItem className="align-items-start">
        <div>
          <div className="font-strong">Payment</div>
          <div className="fg-subtle">Add a card or payment method.</div>
        </div>
      </StepperItem>
    </Stepper>
  )
}

Horizontal layout

Set layout="horizontal" to lay out steps side-by-side instead of stacking them vertically. The connecting track and status indicator realign automatically for the row orientation.

  1. Account
  2. Shipping
  3. Payment
  4. Review
JSX
import { Stepper, StepperItem } from '@chassis-ui/react'

export const Example = () => {
  return (
    <Stepper layout="horizontal">
      <StepperItem>Account</StepperItem>
      <StepperItem>Shipping</StepperItem>
      <StepperItem active>Payment</StepperItem>
      <StepperItem>Review</StepperItem>
    </Stepper>
  )
}

Responsive variants

Pass a breakpoint-prefixed value — sm:horizontal through 2xl:horizontal — to switch from vertical to horizontal only from that breakpoint up. The stepper needs a parent carrying the .contains-inline utility class, since the modifier switches layout through a container query rather than a viewport media query.

  1. Account
  2. Shipping
  3. Payment
  4. Review
JSX
import { Stepper, StepperItem } from '@chassis-ui/react'

export const Example = () => {
  return (
    <div className="contains-inline">
      <Stepper layout="sm:horizontal">
        <StepperItem>Account</StepperItem>
        <StepperItem>Shipping</StepperItem>
        <StepperItem active>Payment</StepperItem>
        <StepperItem>Review</StepperItem>
      </Stepper>
    </div>
  )
}

Icon steps

Set icon to replace the numeric counters with status icons. Past, current, and upcoming steps each render a different default icon, driven by the --status-icon custom property.

  1. Account
  2. Shipping
  3. Payment
  4. Review
JSX
import { Stepper, StepperItem } from '@chassis-ui/react'

export const Example = () => {
  return (
    <Stepper layout="horizontal" icon>
      <StepperItem>Account</StepperItem>
      <StepperItem active>Shipping</StepperItem>
      <StepperItem>Payment</StepperItem>
      <StepperItem>Review</StepperItem>
    </Stepper>
  )
}

Overflow

Set overflow to wrap the stepper in a horizontally scrollable container. Wide horizontal steppers with many or long-labeled steps keep each step's natural width instead of shrinking to fit their parent.

  1. First step label
  2. Past step label
  3. Steps keep their natural width
  4. Next step label
  5. Last step label
JSX
import { Stepper, StepperItem } from '@chassis-ui/react'

export const Example = () => {
  return (
    <Stepper layout="horizontal" overflow>
      <StepperItem>First step label</StepperItem>
      <StepperItem>Past step label</StepperItem>
      <StepperItem active>Steps keep their natural width</StepperItem>
      <StepperItem>Next step label</StepperItem>
      <StepperItem>Last step label</StepperItem>
    </Stepper>
  )
}

Context colors

Set color on <Stepper> to tint every step with one of the Chassis context colors. The active step receives the strongest tint of that color family automatically.

  1. Account
  2. Shipping
  3. Payment
  4. Review
JSX
import { Stepper, StepperItem } from '@chassis-ui/react'

export const Example = () => {
  return (
    <Stepper layout="horizontal" color="secondary">
      <StepperItem>Account</StepperItem>
      <StepperItem>Shipping</StepperItem>
      <StepperItem active>Payment</StepperItem>
      <StepperItem>Review</StepperItem>
    </Stepper>
  )
}

Set color on an individual <StepperItem> (or color in a data-driven item definition) to give that one step its own semantic color, independent of its neighbors — useful when steps in a timeline represent distinct outcomes rather than one continuous sequence.

  1. Order placed
  2. Payment confirmed
  3. Preparing shipment
  4. Delivery delayed
JSX
import { Stepper, StepperItem } from '@chassis-ui/react'

export const Example = () => {
  return (
    <Stepper layout="horizontal">
      <StepperItem color="success">Order placed</StepperItem>
      <StepperItem color="success">Payment confirmed</StepperItem>
      <StepperItem active color="info">
        Preparing shipment
      </StepperItem>
      <StepperItem color="danger">Delivery delayed</StepperItem>
    </Stepper>
  )
}

Interactive steps

Render each step as a link or button when it should navigate to another page or trigger an in-page action — set href or component="button" on <StepperItem>, or href on a data-driven item definition. Because <a>/<button> elements aren't valid children of an <ol>, <Stepper> automatically renders its root as a <div> instead whenever a step is interactive — pass component explicitly if you need to override that.

JSX
import { Stepper, StepperItem } from '@chassis-ui/react'

export const Example = () => {
  return (
    <nav aria-label="Progress">
      <Stepper>
        <StepperItem href="#">Account</StepperItem>
        <StepperItem href="#" active>
          <span className="visually-hidden">Current step: </span>Shipping
        </StepperItem>
        <StepperItem href="#">Payment</StepperItem>
      </Stepper>
    </nav>
  )
}

Accessibility

The active step should carry aria-current="step" so its position is conveyed to assistive technology beyond color and layout alone — <StepperItem active> and a data-driven item with active: true set this automatically. Interactive steppers built from links or buttons additionally need a visually hidden prefix (such as "Current step: ") on the active item's label, since a pseudo-element counter and color alone aren't reliably announced by screen readers.

Wrap an interactive stepper in a <nav> element with a descriptive aria-label (such as "Progress") to expose the sequence as a landmark for screen reader navigation. The default, list-based stepper doesn't need this wrapper — an <ol>/<li> structure already carries native list semantics.

See the ARIA aria-current specification and the WAI-ARIA Authoring Practices Guide for further guidance.

API

Stepper

PropTypeDefault
asChild?boolean—
Render the single child element in place of this component's own element, with this component's classes, props and ref merged onto it: <Button asChild><Link href="/login">Log in</Link></Button>. The alternative to component for React Server Components — an element can be passed from the server to this package's client components, a component reference (component={Link}) can't.
className?string—
A string of all className you want applied to the component.
color?ContextColor—
Sets the color of the component to one of Chassis context colors.
component?ElementType—
Component used for the root node. Either a string to use a HTML element or a component. Defaults to 'ol', unless a step (a data-driven item with href, or a <StepperItem> child rendering an <a>/<button> through component or asChild) is interactive — a bare <a>/ <button> isn't a valid direct child of <ol>, so the default switches to 'div' instead. Pass component explicitly to opt out of this.
icon?boolean—
Replaces the step counters with icons, driven by the --status-icon custom property.
items?StepperItemDef[]—
Array of step definitions for data-driven rendering. When provided, children are ignored.
layout?"horizontal" | "sm:horizontal" | "md:horizontal" | "lg:horizontal" | "xl:horizontal" | "2xl:horizontal"—
Lays out steps side-by-side instead of stacking them vertically, either unconditionally or from a given breakpoint up.
overflow?boolean—
Wraps the stepper in a horizontally scrollable container so steps keep their natural width instead of shrinking to fit.
ref?any—

StepperItemDef

Shape of each entry in Stepper's items array (data-driven rendering).

PropTypeDefault
active?boolean—
Marks the item as the current step.
color?ContextColor—
Sets the color of the item.
href?string—
Renders the item as a link to the given URL.
labelReactNode—
Step label content.

StepperItem

PropTypeDefault
active?boolean—
Marks the item as the current step.
asChild?boolean—
Render the single child element in place of this component's own element, with this component's classes, props and ref merged onto it: <Button asChild><Link href="/login">Log in</Link></Button>. The alternative to component for React Server Components — an element can be passed from the server to this package's client components, a component reference (component={Link}) can't.
className?string—
A string of all className you want applied to the component.
color?ContextColor—
Sets the color of the component to one of Chassis context colors.
component?ElementType—
Component used for the root node. Either a string to use a HTML element or a component.
href?string—
The href attribute for an interactive step rendered as a link. Renders an <a> in place of the <li>, unless component or asChild chose the element. Inside a Stepper, the stepper renders a <div> around it in place of the <ol>.
ref?any—