Skip to content

Stepper

Show progress through a sequence, including completed, current, upcoming and failed steps.

import { Stepper, StepperDetour, StepperStep } from '@youj/react'

Installation & usage

npm i @youj/react

For an app already set up with npx @youj/cli init, which installs the peers (@youj/core, @youj/motion, motion and Base UI) and wires the provider. New app? Start at Installation.

Use a stepper to show progress through a fixed sequence people move through in order: checkout, onboarding, a setup flow. It shows state, it doesn't hold the steps' content. For a flow that shows one step at a time with Back and Continue built in, use a Wizard, which draws its own stepper. For a flow that spans pages, with steps as links, use Stepper Nav. For progress that has no named steps, use a Progress bar.

import { Stepper, type StepperStep } from '@youj/react'

const steps: StepperStep[] = [
  { id: 'cart', title: 'Cart' },
  { id: 'shipping', title: 'Shipping' },
  { id: 'payment', title: 'Payment' },
  { id: 'review', title: 'Review' },
]

export function CheckoutProgress({ current }: { current: number }) {
  return <Stepper steps={steps} current={current} aria-label="Checkout" />
}

current is the index of the step in progress. Steps before it are complete, and current={steps.length} marks every step done. The current step's disc is the solid one, in the accent; done steps step back to a tint with their check, and upcoming ones are outlined. Pass onStepClick and done steps, and the current one, become buttons that go back to them. Upcoming steps stay inert, so the stepper can't skip ahead of the flow's own validation.

Reference

Propssteps, current, orientation… (12)

Props

Stepper props
PropType
stepsRequiredreadonly StepperStep[]–
currentRequirednumber

Index of the current step. Steps before it are complete; steps.length means all are done.

–
orientation'horizontal' | 'vertical'

default 'horizontal'

'horizontal'
onStepClick(index: number) => void

Makes done steps (and the current one) buttons that go back to them.

–
stepHref(index: number) => string | undefined

Makes done steps (and the current one) links to their pages instead, for flows that span pages. onStepClick still fires on click.

–
compactboolean

Hides titles and descriptions below the sm breakpoint (horizontal only), leaving the discs and connectors.

default false

false
detourStepperDetour

A rerouted stretch, drawn as an arc from one step over the ones it bypasses to the step it rejoins. Horizontal only.

–
Other props pass through: extends ComponentProps<'ol'>.

Steps

Each entry in steps is a StepperStep:

StepperStep props
PropType
idRequiredstring

A stable key.

–
titleRequiredReactNode

The step's name.

–
descriptionReactNode

A second line under the title.

–
optionalboolean

Adds "Optional" under the title.

–
invalidboolean

Marks the step as failed (e.g. its validation did not pass).

–
flaggedboolean

Calls a step out without failing it (a deadline, a milestone to watch): until it's done its disc wears a flag and its description the danger ink. The flag is decorative, so say why in the description.

–
MotionConnector fill, disc, title color… · reduced motion

Motion

Motion tokens
MomentTokens
Connector fillscales from the inline start (or down) when the step before it is done, and drains back if you return
duration.slowease.standard

340ms · cubic-bezier(0.2, 0, 0, 1)

340mscubic-bezier(0.2, 0, 0, 1)
Discbackground, border and ring change as the step changes state
duration.baseease.standard

220ms · cubic-bezier(0.2, 0, 0, 1)

220mscubic-bezier(0.2, 0, 0, 1)
Title color
duration.baseease.standard

220ms · cubic-bezier(0.2, 0, 0, 1)

220mscubic-bezier(0.2, 0, 0, 1)
Check and warning marksYouj icons draw on when they mount
no token

Detourthe arc draws from the step it leaves, and its label rises in as the line arrives
duration.deliberateease.emphasized

560ms · cubic-bezier(0.6, 0, 0.1, 1)

560mscubic-bezier(0.6, 0, 0.1, 1)

Reduced motion

Connectors fill and drain at once, and marks appear without drawing.

The fill is a transform on a Motion value, so going back mid-fill drains from where the fill is instead of restarting.

Reduced motion

Connectors fill and drain instantly, discs change color at once, the check and warning marks appear without drawing and a detour is drawn in place. The state reads exactly the same.

AccessibilityARIA, focus, announcements

Accessibility

  • The stepper is an ordered list named "Progress" by default. Name it after the flow with aria-label.
  • The current step has aria-current="step", and every step's state is written out after its title for screen readers: "Payment, has an error".
  • With onStepClick, the done steps and the current one are real buttons, and with stepHref they're links. Upcoming steps aren't focusable.
  • The discs, connectors and marks are decorative (aria-hidden); the text carries the state.
  • A detour's label is read after the step it leaves, with where it rejoins ("Detour, rejoins at step 4", stepDetour in the labels).