Stepper
Progress through a known sequence of steps, as a list with the current step marked; your application moves between steps.
A stepper shows where the reader is in a flow of a few steps: an import, an application, a set-up. It only shows: the buttons that move on and back sit below the content, in FormActions. For the steps of a task that runs by itself, use Trace; for numbered instructions, use Steps.
- Choose the file, completed
- Match the columns
- Check the rows
- Import
Step 2 of 4: Match the columns
// apps/docs/examples/ui/stepper.tsx'use client';
import {useState} from 'react';
import {FormActions} from '@workspace/active-ui/components/composed/form-actions';import {Stepper} from '@workspace/active-ui/components/composed/stepper';import {Button} from '@workspace/active-ui/components/primitives/button';
const STEPS = [ {id: 'file', label: 'Choose the file'}, {id: 'columns', label: 'Match the columns'}, {id: 'check', label: 'Check the rows'}, {id: 'import', label: 'Import'},];
/** * Progress through an import of four steps. The stepper only shows where * you are; the buttons below it move on and back. After the last step every * step counts as completed. */export default function StepperExample() { const [step, setStep] = useState(1); return ( <div className="grid w-full max-w-2xl gap-6"> <Stepper steps={STEPS} currentStep={step} completedLabel="completed" aria-label="Import progress" /> <p className="text-muted-foreground text-sm" aria-live="polite"> {step < STEPS.length ? `Step ${step + 1} of ${STEPS.length}: ${STEPS[step]!.label}` : 'The import is done.'} </p> <FormActions> <Button variant="secondary" disabled={step === 0} onClick={() => setStep((current) => current - 1)} > Previous </Button> <Button disabled={step === STEPS.length} onClick={() => setStep((current) => current + 1)} > {step === STEPS.length - 1 ? 'Finish' : 'Next'} </Button> </FormActions> </div> );}What the composition adds#
State from one number. Steps before
currentare completed, the step at it is current, later steps are upcoming.Step currentequal to the number of steps means everything is done.Step A real list. An
<ol>witharia-on the current step; a completed step carries a check mark and a hidden word, the current step a filled marker and a heavier label, so no state rests on colour alone.current="step" Narrow screens stack the steps; from the
smwidth they sit in a row that wraps, joined by lines. There is no orientation prop.
Steps are not clickable: which steps the reader may go back to is your flow's rule.
Usage#
import {Stepper} from '@workspace/active-ui/components/composed/stepper';
<Stepper steps={[ {id: 'file', label: 'Choose the file'}, {id: 'check', label: 'Check the rows'}, ]} currentStep={step} completedLabel="completed" aria-label="Import progress"/>;What your application provides#
The steps with a stable
idand a translatedlabel, the current step, and the navigation.The translated word for completed steps, read by screen readers after the label.
A name for the list (
aria-), and an announcement of the new step where moving does not move focus, as the example does with a polite line of text.label
API#
Import from @workspace/
Props
- Type
string
The translated word for "completed", for screen readers only.
Required, for the same reason as the required label of Status: the check mark is a shape and not a word, so without this text the difference between a completed and an upcoming step would only be visible. This component does not translate, so the text comes from the caller.
- Type
number
Zero-based index of the current step.
Everything before it counts as completed, everything after it as upcoming. A value equal to steps. means everything is completed and there is no current step any more; that is a valid end state, not an error.
- Type
readonly StepperStep[]
The steps in order. At least one.
Other props, such as the standard attributes of the underlying element, className and ref, are passed through.
Types
One step in a Stepper.
id is the identity and label the translated text. That separation is the same as in Search ({value, label}) and for the same reason: identity must never run on a translated label, because then a step changes identity as soon as the user picks another language. The key is also what a caller with conditional steps needs: a sequence that changes length reconciles wrongly on an index.
interface StepperStep {
id: string;
label: string;
}The props of Stepper, each described in its prop list on this page.
interface StepperProps extends Omit<
React.ComponentProps<'ol'>,
'children'
> {
steps: readonly StepperStep[];
currentStep: number;
completedLabel: string;
}