Stepper
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.
- Account
- Shipping
- Payment
- Review
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.
- Account
- Shipping
- Payment
- Review
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.
- AccountCreate a username and password.
- ShippingWhere should the order ship?
- PaymentAdd a card or payment method.
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.
- Account
- Shipping
- Payment
- Review
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.
- Account
- Shipping
- Payment
- Review
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.
- Account
- Shipping
- Payment
- Review
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.
- First step label
- Past step label
- Steps keep their natural width
- Next step label
- Last step label
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.
- Account
- Shipping
- Payment
- Review
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.
- Order placed
- Payment confirmed
- Preparing shipment
- Delivery delayed
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.
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
| Prop | Type | Default |
|---|---|---|
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).
| Prop | Type | Default |
|---|---|---|
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. | ||
label | ReactNode | — |
| Step label content. | ||
StepperItem
| Prop | Type | Default |
|---|---|---|
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 | — |