Skip to content

Stepper Nav

Navigate a flow that spans pages, with links back to completed steps.

import { StepperNav } 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 it at the top (or down the side) of a flow where each step is its own page: checkout, onboarding, workspace setup. It shows where the reader is and lets them go back to any step they've done. It doesn't run the flow: validation, saving and what "Continue" does stay with your app, and upcoming steps can't be jumped to. When the steps live on one page and the component should own them, use a Wizard. To show progress without links, use a Stepper on its own.

import { StepperNav } from '@youj/react'

const steps = [
  { id: 'account', title: 'Account', description: 'Email and password' },
  { id: 'workspace', title: 'Workspace', description: 'Name and URL' },
  { id: 'plan', title: 'Plan' },
  { id: 'invite', title: 'Invite', optional: true },
]

export function SetupProgress({ step }: { step: number }) {
  return <StepperNav aria-label="Setup" steps={steps} current={step} href={(i) => `/setup/${steps[i]!.id}`} />
}

current is the index of the step the reader is on, usually read from the route. href maps a step to its page. The done steps and the current one become links, and the ones ahead stay plain text. Set current to steps.length once the flow is done, and every step shows its check.

Each step is a Stepper step: a title, an optional description, optional to add "Optional" under the title, and error to mark a step that needs another look. onNavigate fires as a step link is followed, which is where a client-side router takes over.

Reference

Propssteps, current, href… (6)

Props

StepperNav props
PropType
stepsRequiredreadonly StepperStep[]–
currentRequirednumber

Index of the current step.

–
hrefRequired(index: number) => string

The page of step index. Done steps and the current one link to it.

–
onNavigate(index: number) => void

Fires as a step link is followed, e.g. for client-side routing.

–
orientation'horizontal' | 'vertical'

default 'horizontal'

'horizontal'
compactboolean

Hide titles below sm (horizontal only).

default true

true
Other props pass through: extends ComponentProps<'nav'>.
MotionConnector fills, connector drains, check… · reduced motion

Motion

The motion is Stepper's.

Motion tokens
MomentTokens
Connector fillsas a step completes, scaling along the line from the finished step
duration.slowease.standard

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

340mscubic-bezier(0.2, 0, 0, 1)
Connector drainswhen you go back, from wherever the fill is
duration.slowease.standard

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

340mscubic-bezier(0.2, 0, 0, 1)
Checkdraws in on a finished step
duration.slowease.emphasized

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

340mscubic-bezier(0.6, 0, 0.1, 1)
Disc and title colorupcoming, current, complete or error
duration.baseease.standard

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

220mscubic-bezier(0.2, 0, 0, 1)

Reduced motion

Connectors fill and drain at once, and checks appear already drawn.

Reduced motion

The connectors fill and drain at once, and a finished step's check appears already drawn. Colors change instantly. The links, statuses and names are unchanged.

AccessibilityARIA, focus

Accessibility

  • It's a nav landmark named "Progress" by default. Name it after the flow with aria-label ("Setup", "Checkout").
  • The steps are an ordered list. The current step carries aria-current="step", and every step's name ends with its status ("Workspace, current step", "Account, completed", "Plan, not started"), so the progress reads the same without the colors.
  • Done steps and the current one are links. Upcoming steps aren't focusable, since they can't be reached yet.
  • Titles hidden below sm are still in the accessible name.