Components · Workflow
Stepper
Multi-step progress for wizards and onboarding: horizontal or vertical, current, complete, error and optional steps.
Wizard with clickable completed steps
import { useState } from 'react';
import { Button } from '@rookcom/monarch-react';
import { Stepper, type StepperStep } from '@rookcom/monarch-react/patterns';
const SETUP: StepperStep[] = [
{ id: 'basics', label: 'Basics' },
{ id: 'access', label: 'Access' },
{ id: 'branding', label: 'Branding', optional: true },
{ id: 'review', label: 'Review' },
];
function WizardDemo() {
const [current, setCurrent] = useState(1);
const [furthest, setFurthest] = useState(1);
const go = (i: number) => {
setCurrent(i);
setFurthest((f) => Math.max(f, i));
};
return (
<div style={{ display: 'grid', gap: 16, width: '100%' }}>
<Stepper
aria-label="Setup progress"
steps={SETUP}
current={current}
furthest={furthest}
onStepSelect={(_, i) => go(i)}
/>
<div style={{ display: 'flex', gap: 8 }}>
<Button disabled={current === 0} onClick={() => go(current - 1)}>
Back
</Button>
<Button
variant="primary"
disabled={current === SETUP.length - 1}
onClick={() => go(current + 1)}
>
Continue
</Button>
</div>
</div>
);
}
<WizardDemo />Usage examples
Error and descriptions
import { Stepper } from '@rookcom/monarch-react/patterns';
<Stepper
aria-label="Import progress"
current="map"
steps={[
{ id: 'upload', label: 'Upload', description: 'contacts.csv' },
{
id: 'map',
label: 'Map columns',
description: '2 columns need a field',
status: 'error',
},
{ id: 'preview', label: 'Preview' },
{ id: 'import', label: 'Import' },
]}
/>Vertical
import { Stepper } from '@rookcom/monarch-react/patterns';
<Stepper
orientation="vertical"
aria-label="Domain setup"
current={2}
steps={[
{ id: 'add', label: 'Add domain', description: 'help.example.com' },
{ id: 'dns', label: 'Add DNS records' },
{ id: 'verify', label: 'Verify', description: 'Checking every few minutes' },
{ id: 'live', label: 'Live' },
]}
/>Compact
import { Stepper, type StepperStep } from '@rookcom/monarch-react/patterns';
const SETUP: StepperStep[] = [
{ id: 'basics', label: 'Basics' },
{ id: 'access', label: 'Access' },
{ id: 'branding', label: 'Branding', optional: true },
{ id: 'review', label: 'Review' },
];
<Stepper aria-label="Checkout progress" compact="always" current={1} steps={SETUP} />Purpose
Shows where the user is in a fixed sequence of steps, what is done and what is left, and optionally lets them jump back to a finished step. Import from @rookcom/monarch-react/patterns.
Use when
- A wizard or setup flow with three to seven named steps (create a community, import a file, connect a domain).
- A record that moves through ordered stages, shown read-only (a payroll run, a filing journey).
Avoid when
- Sections the user can visit in any order: use Tabs.
- Progress of one task with no named steps: use Progress.
- A to-do list of independent tasks: use a checklist.
Design principles
- Status is never colour alone: a check for complete, “!” for error, the number otherwise, plus hidden text (“completed”, “needs attention”).
- The current step is the strongest element: accent ring and semibold label.
- Connectors fill with the accent as steps complete, so progress reads at a glance.
- Navigation is opt-in: only with
onStepSelectdo reachable steps become buttons; the current and future steps never are. - When there is no room it says less, not smaller: “Step 2 of 5 · Access” with a bar.
API reference
import { Stepper, stepStatus } from '@rookcom/monarch-react/patterns';| Export | Kind | Rendering | Root class |
|---|---|---|---|
Stepper | component | server-safe | .mn-stepper |
stepStatus | utility | server-safe | – |
Renders <nav aria-label="Progress"> (ref) with an <ol>; each step is an <li> with data-status and, for the current step, aria-current="step".
steps are { id, label, description?, optional?, status?, disabled? }; status overrides the derived state with error or complete.
current and furthest take a step id or a zero-based index. stepStatus(index, currentIndex, step) is exported to derive the same status elsewhere.
Generated from the TypeScript declarations of packages/react/src/components/workflow/stepper.
Properties
Stepper
Multi-step progress for wizards, onboarding and setup flows. An ordered list inside a `nav` with `aria-current="step"` on the current step; finished steps can be made clickable. Collapses to "Step 2 of 5" with a progress bar when there is no room.
| Prop | Type | Description |
|---|---|---|
current required | string | number | The current step: its `id` or its zero-based index. Steps before it are complete. |
steps required | StepperStep[] | The steps, in order. |
className | string | |
compact | "auto" | "always" | "never" | When to show the compact "Step 2 of 5" summary with a progress bar instead of the full list. `auto` (default) switches when a horizontal stepper's own width is under 36rem, so it collapses at 320px and in narrow panels. |
furthest | string | number | The furthest step the user has reached (id or index), when they have gone back. Steps up to it stay clickable. Default: the current step. |
id | string | |
messages | Partial<StepperMessages> | Override visible and announced strings. |
onStepSelect | ((step: StepperStep, index: number) => void) | Makes reachable steps (complete ones and any up to `furthest`) buttons that call this with the step and its index. Without it the stepper only shows progress. |
orientation | "horizontal" | "vertical" | Default `horizontal`. A vertical stepper suits a sidebar or a long setup checklist. |
Variants
| Prop | Values | Description |
|---|---|---|
orientation | 'horizontal''vertical' | horizontal (default) spreads steps across with connectors; vertical stacks them for sidebars and checklists, and never collapses. |
compact | 'auto''always''never' | auto (default) collapses a horizontal stepper to the “Step n of m” summary when its own width is under 36rem (a container query), so it adapts to 320px screens and narrow panels. |
States
| State | Exposed as | Behaviour |
|---|---|---|
| Current | data-status="current", aria-current="step" | Accent ring and number, semibold label. |
| Complete | data-status="complete" | Filled accent circle with a check; the following connector is accent. |
| Upcoming | data-status="upcoming" | Outlined circle with the number, muted label. |
| Error | data-status="error" | Filled danger circle with “!”, danger label. |
| Optional | – | “Optional” under the label (messages.optional). |
| Clickable | button[data-clickable] | Hover fill and underline; focus ring. |
| Disabled | data-disabled | A step that would be clickable but is disabled: disabled label colour. |
Interaction behaviour
- Steps before
currentare complete. WithonStepSelect, complete steps and steps up tofurthest(after going back) are buttons that call it with the step and its index. - The current step, future steps beyond
furthestanddisabledsteps are plain text. - The stepper does not move focus; on a step change, move focus to the new step’s heading in your page so screen-reader users hear where they are.
Keyboard support
| Keys | Action |
|---|---|
| Tab | Moves between clickable steps (only reachable steps are buttons). |
| Enter / Space | Goes to the focused step. |
A read-only stepper has no tab stops.
Accessibility
- Role
- navigation containing an ordered list
- Accessible name
aria-labelon the nav, default “Progress”; name it after the flow (“Setup progress”).
aria-current="step"marks the current step.- Each step includes hidden text “Step n of m:” and, when relevant, “(completed)” or “(needs attention)”, so buttons are fully named.
- Indicator numbers, checks and the compact bar are
aria-hidden; the text carries the information. - In compact mode the list is not displayed and the summary text (“Step 2 of 5 Access”) is read instead.
- Verified with axe in jsdom and in Chromium, Firefox and WebKit on /components/stepper.
Responsive behaviour
- Horizontal steps share the width; labels wrap rather than overflow.
- Under 36rem of its own width (
compact="auto") it shows “Step 2 of 5 · label” and a progress bar, so at 320px nothing is cramped. - Vertical steppers keep the full list at every width.
Theme support
- Monarch tokens only:
--mn-color-accent-solidfor complete and current,--mn-color-danger-solidfor errors,--mn-color-border-strongfor upcoming outlines. - Follows the product accent; forced-colours mode draws indicators in Highlight.
- The hover transition is removed under
prefers-reduced-motion: reduce.
Do and don't
Do
- Keep labels to one or two words, and keep the step count small (three to seven).
- Mark steps with problems
status: "error"and jump the user to the first one on submit. - Pair the stepper with Back / Continue buttons; the stepper is not the only way forward.
Don't
- Do not make future steps clickable when earlier steps validate what they need.
- Do not use a stepper for tabs or for free-order sections.
- Do not rely on the step colour to explain an error; say what is wrong in the step itself.
Related components
- Progress: Progress of one task without named steps; also the bar in compact mode.
- Tabs: Sections in free order.
- OnboardingChecklist: Independent tasks the user ticks off in any order.
Dependencies
| Kind | Depends on |
|---|---|
| npm packages | react |
| Monarch components | Progress |
Testing coverage
- Named nav and ordered list;
aria-current="step"; complete/current/upcoming statuses; hidden “Step n of m” and status text; description and optional note; axe. - Current by index (clamped) and by id.
- Error and out-of-order complete overrides; axe.
- No buttons without
onStepSelect; with it only completed steps are buttons, clicked or activated with Enter; axe. furthestkeeps later reached steps clickable after going back; disabled steps are not.- Compact summary with an aria-hidden bar; vertical orientation.
- Translated messages;
stepStatus; renders on the server.
| Check | Covered |
|---|---|
| Axe in jsdom | Yes |
| Server rendering | Yes |
| Browser suites | Visual baselines: /__gallery/stepper; Cross-browser axe: /components/stepper |
Tests: packages/react/src/components/workflow/stepper