Primitives

Reference the unstyled components for custom stepper UI.

Primitives

Every definition exposes bound primitives. They are generated from your stepper definition, so the components know your exact step ids.

Example setup

All examples on this page use this stepper definition:

import { defineStepper } from "@stepperize/react";

const checkout = defineStepper([
  { id: "shipping", title: "Shipping", description: "Delivery address" },
  { id: "payment", title: "Payment", description: "Payment method" },
  { id: "review", title: "Review", description: "Confirm order" },
]);
const { Stepper } = checkout;

Use them when you want accessible stepper UI without giving up your own styles.

They are bound to the definition, so all step props and item data use the ids from that flow.

Root

Stepper.Root creates and provides one stepper instance.

<Stepper.Root defaultStep="shipping" linear>
  {({ stepper }) => <p>{stepper.current.title}</p>}
</Stepper.Root>

It accepts the same options as useStepper, plus orientation.

orientation is exposed as data-orientation on the root. Stepper.List also accepts orientation for keyboard behavior and aria-orientation.

List

<Stepper.List>
  <Stepper.Items>
    {(step) => (
      <Stepper.Item key={step.id}>
        <Stepper.Trigger>
          <Stepper.Indicator />
          <Stepper.Title>{step.title}</Stepper.Title>
          <Stepper.Description>{step.description}</Stepper.Description>
        </Stepper.Trigger>
      </Stepper.Item>
    )}
  </Stepper.Items>
</Stepper.List>

Inside Stepper.Items, Stepper.Item gets the current item from context. Outside the iterator, pass a step id:

<Stepper.Item step="payment">
  <Stepper.Trigger>Payment</Stepper.Trigger>
</Stepper.Item>

Content and actions

<Stepper.Content step="shipping">Shipping form</Stepper.Content>
<Stepper.Content step="payment">Payment form</Stepper.Content>
<Stepper.Content step="review">Review order</Stepper.Content>

<Stepper.Actions>
  <Stepper.Prev>Back</Stepper.Prev>
  <Stepper.Next>Continue</Stepper.Next>
</Stepper.Actions>

Prev and Next use the same rules as stepper.canPrev and stepper.canNext. When linear is enabled, Trigger and List keyboard navigation use stepper.canGoTo(id).

Next and Prev call stepper.next() and stepper.prev() without a payload. For forms that need to submit values before moving, use your own button and call the instance directly.

Components

ComponentPurpose
RootProvider-backed root.
ListStep list container.
ItemsTyped iterator over steps.
ItemPer-step wrapper.
TriggerButton that selects a step.
Title / DescriptionStep labels.
IndicatorNumber, icon, or status mark.
SeparatorDecorative separator.
ContentActive-step panel.
ActionsNavigation controls wrapper.
PrevPrevious button.
NextNext button.

Custom render

Most primitives accept render. It replaces the primitive's root element, so always spread the props:

<Stepper.Trigger
  render={(props) => (
    <button className="step-trigger" {...props}>
      Shipping
    </button>
  )}
/>

The props include accessibility attributes, event handlers, and data attributes.

Data attributes

The primitives expose their state through three data-* attributes, which is how you style them:

AttributeValuesSet on
data-statusactive · previous · upcomingItem, Trigger, Indicator
data-orientationhorizontal · verticalRoot, List, Separator
data-componentstepper, stepper-list, stepper-item, stepper-trigger, stepper-contentevery primitive

For how to style against these — Tailwind variants, group-data, plain CSS, and the status-vs-completion gotcha — see the Styling guide.

Item state

Use the root render prop when item UI needs instance state:

<Stepper.Root>
  {({ stepper }) => (
    <Stepper.List>
      <Stepper.Items>
        {(step, index) => (
          <Stepper.Item>
            <Stepper.Trigger>{index + 1}</Stepper.Trigger>
          </Stepper.Item>
        )}
      </Stepper.Items>
    </Stepper.List>
  )}
</Stepper.Root>
Edit on GitHub

Last updated on

On this page