Shows how far a procedure has come: which steps are done, which one is being worked on, which lie ahead and which failed (also called a progress tracker). Reach for it for approvals, rollouts and closing routines.
import { Stepper } from "@umriss-ui/core";
Each state is said as a word as well as drawn, so a screen reader hears "done", "current step", "upcoming" or "failed". A failed step stays failed when the procedure moves past it; past the last step, every step is done.
Pass the steps in order and the index of the current one; the steps before it read as done, the ones after as upcoming.
import { Stepper } from "@umriss-ui/core";
export default function WhereAProcedureStands() {
return (
<Stepper
aria-label="Incident INC-1048"
steps={[{ label: "Detect" }, { label: "Acknowledge" }, { label: "Mitigate" }, { label: "Resolve" }, { label: "Review" }]}
current={2}
style={{ maxWidth: 720 }}
/>
);
}
Set orientation to vertical where each step needs a description beneath its label, such as a deadline.
import { Stepper } from "@umriss-ui/core";
const CLOSE = [
{ label: "Post all invoices", description: "Until Tuesday, 3 March" },
{ label: "Reconcile accounts", description: "Bank, payables, receivables" },
{ label: "Book accruals", description: "Bonuses and open orders" },
{ label: "Report to the board", description: "Friday, 6 March" },
];
export default function StepsInAColumn() {
return <Stepper aria-label="Closing February" orientation="vertical" steps={CLOSE} current={2} style={{ maxWidth: 360 }} />;
}
Mark a step failed and it shows a cross and says "failed"; it stays failed when the procedure has moved past it.
import { Stepper } from "@umriss-ui/core";
export default function AFailedStep() {
return (
<Stepper
aria-label="Shipment FP-1004249"
orientation="vertical"
steps={[
{ label: "Loaded", description: "North depot, 06:40" },
{ label: "Out for delivery", description: "Tour T-01" },
{ label: "First attempt", description: "10:35, nobody at the door", failed: true },
{ label: "Second attempt", description: "Tomorrow, 08:00–10:00" },
{ label: "Delivered" },
]}
current={3}
style={{ maxWidth: 360 }}
/>
);
}
The stepper has no keys of its own: your application sets current after its own check, here behind two buttons.
import { useState } from "react";
import { Button, Stack, Stepper } from "@umriss-ui/core";
const STEPS = [{ label: "Choose people" }, { label: "Set capacity" }, { label: "Plan leave" }, { label: "Confirm" }];
export default function MoveOnYourself() {
const [current, setCurrent] = useState(1);
return (
<Stack gap={4} style={{ maxWidth: 560 }}>
<Stepper aria-label="Set up sprint 15" steps={STEPS} current={current} />
<Stack direction="row" gap={2}>
<Button size="sm" variant="secondary" disabled={current === 0} onClick={() => setCurrent(current - 1)}>
Back
</Button>
<Button size="sm" variant="primary" disabled={current === STEPS.length} onClick={() => setCurrent(current + 1)}>
{current >= STEPS.length - 1 ? "Start the sprint" : "Next"}
</Button>
</Stack>
</Stack>
);
}
At a card's head the stepper shows where a rollout stands; a failed step is marked in place and an alert offers the way on.
import { useState } from "react";
import { Alert, Button, Card, CardBody, CardHeader, ProgressBar, Stack, Stepper, Text } from "@umriss-ui/core";
const STEPS = [
{ label: "Build" },
{ label: "Test" },
{ label: "Canary", description: "5 % of traffic, 15 minutes" },
{ label: "Roll out", description: "All regions" },
{ label: "Verify" },
];
export default function ADeployment() {
const [failed, setFailed] = useState(false);
const current = 2;
const steps = STEPS.map((step, index) => (index === current && failed ? { ...step, failed: true } : step));
return (
<Card style={{ maxWidth: 720 }}>
<CardHeader
eyebrow="Checkout · release 2026.03.17-2"
title="Cap payment retries at one"
actions={
<Button size="sm" variant="ghost" onClick={() => setFailed(true)} disabled={failed}>
Fail the canary
</Button>
}
/>
<CardBody>
<Stack gap={5}>
<Stepper aria-label="Deployment" steps={steps} current={current} />
{failed ? (
<Alert
tone="danger"
title="Canary stopped"
actions={
<Button size="sm" variant="secondary" onClick={() => setFailed(false)}>
Retry the canary
</Button>
}
>
The error rate on the canary rose to 2.4 %, above the 1 % the rollout allows.
</Alert>
) : (
<Stack gap={2}>
<Text size="sm">Canary on 5 % of traffic - 6 of 15 minutes</Text>
<ProgressBar value={0.4} label="Canary" valueText="6 of 15 minutes" />
</Stack>
)}
</Stack>
</CardBody>
</Card>
);
}
The keys of Button apply here.
An ordered list, so a screen reader says how many steps there are and which one it reads. The current step carries aria-current="step"; every other step's state follows its label as a word only a screen reader hears, and the drawn number, tick or cross is hidden from it.
It announces nothing when current moves on: a reader hears the new state where it reads the list. A step that must be heard as it fails is said in an Alert; a share of a step's work stands in a ProgressBar, as in the deployment.
Under forced colours the lines between the steps take the system's text colour, and the current step's marker an outline in the selection colour.
StepperProps| Name | Type | Default | Description |
|---|---|---|---|
stepsrequired | readonly StepperStep[] | — | The steps in the order they are walked. Shown in: Where a procedure stands, Steps in a column, A failed step and 6 more |
currentrequired | number | — | The index of the step being worked on. The steps before it are done,
the ones after it upcoming; past the last step, every step is done. Shown in: Where a procedure stands, Steps in a column, A failed step and 6 more |
orientation | "horizontal" | "vertical" | "horizontal" | horizontal in a row, vertical in a column for steps with longer
descriptions.Shown in: Steps in a column, A failed step, An invoice in tabs (Tabs) |
ref | Ref<HTMLOListElement> | — | Receives the component's root element. from RefPropShown in: A row that holds still (Sizes), An anchored surface (Popover), Take the width of the trigger (Popover) and 2 more |
Also takes every attribute of the rendered element.
StepperStep| Name | Type | Default | Description |
|---|---|---|---|
labelrequired | ReactNode | — | What the step is called - a verb for what happens in it. Shown in: Where a procedure stands, A failed step, Import budgets (FileInput) and 1 more |
description | ReactNode | — | A line beneath the label - how long the step takes, what it needs. Shown in: A failed step, An invoice in tabs (Tabs) |
failed | boolean | — | The step failed. It says so whether it is behind the current step or is
the current one: a failed step is not done. Shown in: A failed step, An invoice in tabs (Tabs) |
What umriss deliberately does not build, and why: ADR-0032.