AntaSteps
Switch to dark theme
Search documentation
On this page

Steps

Ordered process navigation built on Tabs. Use Tabs when the views are peers rather than phases in a flow.

Playground

State

Each option needs a unique value, a label, and a state. state supplies the default marker, value controls selection, and disabled controls access.

CompletedCompletedLoadingLoadingErrorErrorIncompleteIncompleteThis work is complete and ready to use.This work is currently running.Resolve this error before continuing.This work has not started yet.
const options = [
{ value: 'completed', label: 'Completed', state: 'completed' },
{ value: 'loading', label: 'Loading', state: 'loading' },
{ value: 'error', label: 'Error', state: 'error' },
{ value: 'incomplete', label: 'Incomplete', state: 'incomplete' },
]
<Steps fill defaultValue="loading" label="Task state" options={options}>
<TabPanel value="completed"><Text size="small">This work is complete and ready to use.</Text></TabPanel>
<TabPanel value="loading"><Text size="small">This work is currently running.</Text></TabPanel>
<TabPanel value="error"><Text size="small">Resolve this error before continuing.</Text></TabPanel>
<TabPanel value="incomplete"><Text size="small">This work has not started yet.</Text></TabPanel>
</Steps>

An incomplete step can still be selected. Add disabled only when it is locked; disabled steps keep their layout space and use the incomplete marker by default. Pass marker or return a value from renderMarker to keep a custom marker. Use loading for work running in the background.

Priority

secondary is the default: selected markers have a subtle fill inside their outline. primary fills the selected marker with its tone and uses a white icon, like a primary Button. Its 2px resting marker uses border-3 and its 2px rail uses border-3; a completed, unselected marker keeps the primary fill at 80% without an outline. Its completed connector moves one step stronger to border-2. tertiary removes the marker stroke and makes its marker the icon size plus 2px on each side.

primaryBuildBuildReviewReviewDeployDeploy
secondaryBuildBuildReviewReviewDeployDeploy
tertiaryBuildBuildReviewReviewDeployDeploy
<Steps
fill
priority="primary"
tone="brand"
defaultValue="review"
label="Deployment progress"
options={[
{ value: 'build', label: 'Build', state: 'completed' },
{ value: 'review', label: 'Review', state: 'loading' },
{ value: 'deploy', label: 'Deploy', state: 'incomplete' },
]}
/>

Tone

Set tone on Steps to color every step’s label, hint, and icon, plus active and completed markers. Inactive incomplete/loading marker rings stay neutral. Set option.tone when one step needs an override. Labels strengthen on hover and selection; hints stay at the muted text-3 tone. In secondary and tertiary, the marker icon always matches the label’s current color; a secondary marker’s resting stroke uses the matching muted border tone. With every priority, the connector after a completed step is one border step stronger: border-2 from the standard border-3 rail. Error markers stay critical and gain a critical fill when selected.

DoneCompleteDone — CompleteValidateChecking dependenciesValidate — Checking dependenciesReviewNeeds attentionReview — Needs attentionDeployWaitingDeploy — WaitingErrorResolve before continuingError — Resolve before continuing
const options = [
{ value: 'done', label: 'Done', hint: 'Complete', state: 'completed' },
{ value: 'current', label: 'Validate', hint: 'Checking dependencies', state: 'loading' },
{ value: 'review', label: 'Review', hint: 'Needs attention', state: 'incomplete', tone: 'warning' },
{ value: 'deploy', label: 'Deploy', hint: 'Waiting', state: 'incomplete', tone: 'info' },
{ value: 'error', label: 'Error', hint: 'Resolve before continuing', state: 'error' },
]
<Steps tone="brand" defaultValue="current" options={options} />

Labels and hints ellipsize within each step. Hovering or focusing a clipped step shows its full label and hint in a tooltip.

Steps and option tones are neutral (default), brand, info, success, warning, and critical. Options inherit the Steps tone until option.tone is set. Omit the Steps tone, or pass an empty string, for neutral; set an option to neutral or an empty string to opt it out of an inherited tone.

Markers and hints

Marker precedence is:

  1. renderMarker(...) result, unless it returns undefined
  2. option.marker
  3. Built-in marker from state

Return null from renderMarker for an intentionally empty ring.

A number marker is shown directly; an icon-shape string renders an Icon. hint adds secondary text.

DraftContent savedDraft — Content savedApprovalWaiting for reviewApproval — Waiting for reviewPublishNot startedPublish — Not started
const options = [
{ value: 'draft', label: 'Draft', hint: 'Saved', state: 'completed', marker: 1 },
{ value: 'approval', label: 'Approval', hint: 'In review', state: 'loading', marker: 'hourglass' },
{ value: 'publish', label: 'Publish', hint: 'Not started', state: 'incomplete', marker: 'send' },
]
<Steps defaultValue="approval" label="Publishing progress" options={options} />

Use renderMarker when a marker depends on the current step state. Its returned node wins; return undefined to fall back to marker, then the state marker.

const options = [
{ value: 'draft', label: 'Draft', state: 'completed', marker: 1 },
{ value: 'approval', label: 'Approval', state: 'loading' },
{ value: 'publish', label: 'Publish', state: 'incomplete', marker: 'send' },
]
<Steps
defaultValue="approval"
label="Publishing progress"
options={options}
renderMarker={(option, { selected }) =>
option.state === 'loading'
? <Icon shape={selected ? 'refresh-ccw-dot' : 'hourglass'} />
: undefined
}
/>

Panels

Add a TabPanel with the same value as its option. Panels stay mounted while inactive. Omit them when a router renders the content.

Selection

Use defaultValue for uncontrolled selection. Use value and onStateChange when the application owns it.

const options = [
{ value: 'build', label: 'Build', state: 'completed' },
{ value: 'setup', label: 'Setup', state: 'loading' },
{ value: 'next', label: 'Next steps', state: 'incomplete' },
]
const [phase, setPhase] = useState('setup')
<Steps
value={phase}
tone="brand"
options={options}
onStateChange={(_event, { next }) => next && setPhase(next)}
/>

The application also owns state changes and decides when to enable future steps. Steps does not advance or complete them automatically.

Flow actions

Place Back and Continue beside Steps so validation and side effects stay in the application.

BuildBuildSetupSetupNext stepsNext steps
BackContinue
const options = [
{ value: 'build', label: 'Build', state: 'completed' },
{ value: 'setup', label: 'Setup', state: 'loading' },
{ value: 'next', label: 'Next steps', state: 'incomplete' },
]
const [phase, setPhase] = useState('setup')
const index = options.findIndex((option) => option.value === phase)
<Steps
value={phase}
options={options}
onStateChange={(_event, { next }) => next && setPhase(next)}
/>
<Button label="Back" disabled={index === 0} onClick={() => setPhase(options[index - 1].value)} />
<Button label="Continue" disabled={index === options.length - 1} onClick={() => setPhase(options[index + 1].value)} />

Size

small, medium (default), and large use 24px, 28px, and 32px markers.

BuildBuildSetupSetupNext stepsNext stepsBuildBuildSetupSetupNext stepsNext stepsBuildBuildSetupSetupNext stepsNext steps
<Steps
size="small"
options={[
{ value: 'build', label: 'Build', state: 'completed' },
{ value: 'setup', label: 'Setup', state: 'loading' },
{ value: 'next', label: 'Next steps', state: 'incomplete' },
]}
/>

Orientation

Horizontal steps scroll when needed. Vertical steps place the active panel before the next step.

BuildBuildSetupSetupNext stepsNext stepsBuild output and artifacts are ready.Preparing the deployment environment.Review the deployment and share the result.
<Steps
orientation="vertical"
options={[
{ value: 'build', label: 'Build', state: 'completed' },
{ value: 'setup', label: 'Setup', state: 'loading' },
{ value: 'next', label: 'Next steps', state: 'incomplete' },
]}
>
<TabPanel value="build"><Text size="small">Build output is ready.</Text></TabPanel>
<TabPanel value="setup"><Text size="small">Preparing the environment.</Text></TabPanel>
<TabPanel value="next"><Text size="small">Review the result.</Text></TabPanel>
</Steps>

Keyboard and accessibility

Pass label to name the tablist. Arrow keys follow the orientation; Home and End move to the edges; Enter and Space activate a focused step. Disabled steps are skipped.

Markers and connectors are decorative. Put important state text in the active panel or an application-owned live region.

Events

Events match Tabs.

CallbackWhenCancelablePayload
onStateChange(event, { next, prev })Before selectionYesNext and previous values
onChange(event)After selectionNoNative change event
onValueChange(event, { value })After selectionNoSelected value

Steps props

Prop Type Default Description
options StepOption[] Ordered process phases.
priority? primarysecondarytertiary 'secondary' Visual emphasis. Primary uses a solid selected marker and a stronger completed connector; secondary is the outlined default; tertiary is compact and borderless.
tone? neutralbrandcriticalinfosuccesswarning 'neutral' Tone applied to every step without its own tone. Error steps stay critical and disabled steps stay neutral.
renderMarker? (option, state) => ReactNode Builds a custom marker from a step and its current state. A returned node replaces marker and the built-in state marker; return undefined to use those fallbacks, or null for an empty ring.
children? ReactNode Optional <TabPanel value="…"> panels, one per tab value. Each is a self-managing <a-tabpanel> that shows itself when its value is the active tab. Omit them to use Tabs as a bare selectable strip. To place panels in a different layout region, or to unmount an inactive panel, drive selection with a controlled value and render the content yourself (see the docs).
value? string Controlled active value — the tab value to mark selected (and, when a <TabPanel value="…"> shares it, the panel to reveal). When set, you own selection: the strip renders exactly what this says, and a user pick only requests a change via onStateChange — apply it by updating this prop. Leave undefined (and use defaultValue) for uncontrolled.
defaultValue? string Initial active value for the uncontrolled case. After first render Tabs owns selection itself.
onStateChange? (event, detail) => void Fired whenever the active tab changes — event-first. detail is { next, prev } (values; null = none). Cancelable: event.preventDefault() vetoes it (uncontrolled), or in controlled mode answer by updating value.
onChange? (event) => void Fired after the active tab changes — a native change event.
onValueChange? (event, attrs) => void Like onChange, but with a { value } snapshot as the 2nd argument.
onFocus? (event) => void Focus entered the strip (any tab) — wired to focusin (focus lands on a tab, not the tablist).
onBlur? (event) => void Focus left the strip entirely — wired to focusout.
label? string Accessible name for the tablist (aria-label).
size? smallmediumlarge 'medium' Size — small 24px · medium 28px · large 32px tall, matching Button's scale (the tab's label leading runs a touch tighter, offset by 1px more block padding per side).
orientation? horizontalvertical 'horizontal' Layout + arrow-key axis. Horizontal ellipsizes labels when tabs overflow (scroll is opt-in via CSS); vertical stacks them.
fill? boolean false Makes horizontal tabs share the available inline space equally.
disabled? boolean Disable the whole strip.
Inherited props (className, id, slot, style, tabIndex, title)
Prop Type Default Description
className? string CSS class on the component's root element (merged with the component's own classes). Use it directly for layout and positioning — grid/flex placement, margins, alignment — rather than wrapping the component in a <div>/<span>.
id? string HTML id attribute.
slot? string Assigns the element to a named <slot> of a parent web component (e.g. slot="header" inside a <Card>, slot="footer" inside a <Dialog>).
style? CSSProperties Inline styles on the component's root element. Set layout/positioning here (or via className) directly on the component instead of adding a wrapper.
tabIndex? number Tab order. Set to -1 to skip the element when tabbing.
title? string HTML title attribute — native browser tooltip on hover.

Plus every standard DOM event handler (onClick, onFocus, onKeyDown, …) and any data-* or aria-* attribute — forwarded as-is to the underlying <a-*> element.

Step option props

Prop Type Default Description
value string Stable phase identity. Values must be unique within the sequence.
label ReactNode Visible phase label. It ellipsizes in a constrained step; its full content is available in a tooltip only when clipped.
state incompleteloadingcompletederror Application-owned process state. Selection and availability are separate; error keeps a critical icon and outline, and selection adds the fill.
hint? ReactNode Secondary text shown below the label. It ellipsizes in a constrained step; its full content is available in a tooltip only when clipped.
tone? neutralbrandcriticalinfosuccesswarning inherits Steps `tone Tone for this phase. It colors the label, hint, marker, and completed connector. Overrides the Steps tone; error steps stay critical and disabled steps stay neutral.
marker? StepMarker Replaces the marker derived from state. A number is shown directly; an Anta icon shape is rendered as an <Icon>.
disabled? boolean Disables this phase. A custom marker or renderMarker result is kept; otherwise it uses the incomplete marker.
className? string CSS class on the option's rendered row.
style? CSSProperties Inline styles on the option's rendered row.

Web Component

For framework-free use, compose the same light DOM that Steps emits. a-steps and its a-step-* children are structural elements styled by the shipped Steps stylesheet. The Tabs Web Component inside owns selection and panel behavior; markers, states, and tones stay in your markup.

import '@antadesign/anta/elements'
import '@antadesign/anta/components/Steps.css'
BuildCompleteSetupIn progressReviewWaitingBuild output is ready.Prepare the environment.Review the result.
<div>
<a-steps>
<a-tabs role="tablist" aria-label="Setup progress" priority="secondary" default-state="setup" data-steps fill noslide>
<a-tab role="tab" value="build" tabindex="0" tone="success">
<a-step-marker aria-hidden="true"><a-icon shape="check"></a-icon></a-step-marker>
<a-step-desc><a-tab-label>Build</a-tab-label><a-step-hint>Complete</a-step-hint></a-step-desc>
</a-tab>
<a-tab role="tab" value="setup" tabindex="-1" tone="brand">
<a-step-marker aria-hidden="true"><a-loader></a-loader></a-step-marker>
<a-step-desc><a-tab-label>Setup</a-tab-label><a-step-hint>In progress</a-step-hint></a-step-desc>
</a-tab>
<a-tab role="tab" value="review" tabindex="-1">
<a-step-marker aria-hidden="true"><a-icon shape="circle-large"></a-icon></a-step-marker>
<a-step-desc><a-tab-label>Review</a-tab-label><a-step-hint>Waiting</a-step-hint></a-step-desc>
</a-tab>
</a-tabs>
<a-tabpanel role="tabpanel" value="build">Build output is ready.</a-tabpanel>
<a-tabpanel role="tabpanel" value="setup">Prepare the environment.</a-tabpanel>
<a-tabpanel role="tabpanel" value="review">Review the result.</a-tabpanel>
</a-steps>
</div>

Styling

Use priority, size, and orientation first. Set tone on each option. Each option also accepts className, style, and data-* attributes. Markers are light DOM; connectors are pseudo-elements.

Dotted connector. For horizontal steps, replace the connector’s solid background with a dotted border:

BuildBuildSetupSetupNext stepsNext steps
.dotted-steps a-tab:not(:last-child)::before {
height: 0;
background: none;
border-block-start: 2px dotted var(--border-2-brand);
}

className on Steps applies to a-steps. .dotted-steps is only the demo scope; use your own selector when applying the rule.

Fill the container. Pass fill to spread horizontal steps across their container. The connectors take the remaining space, placing the middle step in the middle and the last step at the far edge.

BuildBuildReviewReviewDeployDeploy
<Steps
fill
defaultValue="review"
label="Deployment progress"
options={[
{ value: 'build', label: 'Build', state: 'completed' },
{ value: 'review', label: 'Review', state: 'loading' },
{ value: 'deploy', label: 'Deploy', state: 'incomplete' },
]}
/>