Layout & navigation

AppStepper

Where the user is in a multi-step flow — compact inside a card, or wide as a masthead.

<app-stepper><app-step label="…" status="done" /></app-stepper>

<app-stepper> answers three questions at once: where am I, what have I finished, and how much is left. A wizard without one leaves the user unable to judge whether to start now or come back later.

It is one component with two variants, not two components — the states (done / active / pending / error), the tick-instead-of-number rule and the link behaviour are identical; only the layout differs. Either variant can run orientation="vertical".

Playground

Every attribute, live. Each change re-renders the real tag helper on the server, so the markup underneath is the exact call that produced the stepper above it.

<app-stepper active-step="1">
    <app-step label="Add site" />
    <app-step label="Add zones" />
    <app-step label="Risk assessment" />
    <app-step label="Create report" />
</app-stepper>

Compact

The default. A numbered circle above its label, with a connecting line behind the circles. Sized to sit inside a form card, above the fields for the current step.

variant="steps"
A finished step shows a tick rather than its number: the number says which, the tick says done.
Show code
<app-stepper>
    <app-step label="Add site" status="done" />
    <app-step label="Add zones" status="done" />
    <app-step label="Risk assessment" status="active" />
    <app-step label="Create report" />
</app-stepper>

Journey bar

The wide variant — a large dot beside a “STEP 2” index and the step name, in a bordered bar. For a page masthead above the whole flow, where there is room for full labels.

variant="journey"
Below xl the label pairs no longer fit side by side, so the bar becomes a vertical list and the connectors go away.
Show code
<app-stepper variant="journey">…</app-stepper>
Pick by available width, not by importanceThe journey bar needs roughly 900px to show four steps without truncating. In a two-column layout or a drawer, use the compact variant.

Deriving state from active-step

Instead of hand-setting done / active / pending on every step, set a single active-step index and the statuses are derived — steps before it are done, the one at it is active, the rest pending. A step's own explicit status still wins.

active-step="2"
One number to move the whole wizard forward.
Show code
<app-stepper active-step="2">
    <app-step label="Add site" />
    <app-step label="Add zones" />
    <app-step label="Risk assessment" />
    <app-step label="Create report" />
</app-stepper>

A failed step

Give a step status="error" and it turns red with an alert glyph — an invalid or failed stage the user must return to. It carries no aria-current (the user is being sent back, not standing there).

status="error"
Use it for a stage that failed validation, not merely one that is incomplete — that is what pending is for.
Show code
<app-step label="Payment" status="error" />

Descriptions & icons

A step can carry a secondary description line under its label, and its own icon in place of the number. A done or error step keeps its tick / alert — the custom icon shows only while pending or active.

description & icon
Keep descriptions to a few words — the label carries the meaning.
Show code
<app-step label="Payment" description="Card or invoice" icon="credit-card" status="active" />

Vertical

orientation="vertical" stacks the steps down a side rail, the connector running between the circles. For a narrow column, a drawer, or a settings wizard.

orientation="vertical"
The compact variant becomes a rail of circle-plus-label rows; descriptions sit under each label.
Show code
<app-stepper orientation="vertical">
    <app-step label="Account" description="Email & password" status="done" />
    <app-step label="Company" description="Legal name & tax id" status="active" />
    <app-step label="Billing" description="Card or invoice" />
    <app-step label="Review" />
</app-stepper>

Clickable steps

React's onStepClick makes each step a button that reports its index, for a JS-driven wizard whose navigation lives in client state rather than the URL. A server-rendered page has no client state to drive, so there is no Razor equivalent.

No Razor counterpartUse href on each <app-step> for a revisitable step here instead — see “Revisitable steps” below.

Jump to any step

When every stage can be visited in any order, give each <app-step> an href and drive active-step from the request. A click anywhere on a step, its circle, its title or its description, goes straight to that step, forwards or back.

active-step + href
The stepper keeps no state of its own: the server reads the chosen step and sets active-step.
Show code
<app-stepper variant="journey" active-step="@Model.Step">
    <app-step label="Account" href="?step=0" />
    <app-step label="Payment" icon="credit-card" href="?step=1" />
    <app-step label="Delivery" icon="map-pin" href="?step=2" />
    <app-step label="Confirm" icon="check-circle" href="?step=3" />
</app-stepper>

When to use

Use it when

  • A task is split across several screens and the user needs to see the shape of it.
  • Steps have a fixed order and a known count.
  • Finished steps matter — the user may want to go back and check one.

Reach for something else when

  • The sections are alternative views switched freely. → app-tabs
  • It is one long form on a single screen. → .fld-section heading
  • The number of steps is unknown or changes. → a progress message
  • You are showing progress of a background job. → app-spinner

Best practices

Do
Label each step with the action it performs, and mark exactly one step active. Three to five steps is the readable range.
Don't
“Step 2” adds nothing — the circle already says 2. The label is the only place that can tell the user what the step is for.

Every option

The whole surface of the component, one cell per value — every status, both variants, both orientations, and the combinations that behave specially.

variant — both

variant="steps"
variant="journey"

step status — every value

status="done"
status="active"
status="pending"
status="error"

orientation — vertical, both variants

variant="steps" orientation="vertical"
variant="journey" orientation="vertical"

active-step derivation — every index

active-step="0"
active-step="1"
active-step="2"
active-step="3"

per-step icon (done/error override it)

icon="credit-card" (pending)
icon="credit-card" status="done" (icon suppressed by the tick)
icon="credit-card" status="error" (icon suppressed by the alert)

per-step description

variant="steps" — description under the label
variant="journey" — description inside the text block
(no description)

href — plain, link, active link

(no href — renders a div)
href — renders an <a>
href + status="active" — aria-current on the link

onStepClick — React-only

(no Razor equivalent — renders the same as no href)
No Razor counterpartReact's onStepClick has no Razor equivalent — a server-rendered page has no client state to drive. Use href for a revisitable step there instead.

empty steps

<app-stepper />

Attributes

AttributeTypeDefaultDescription
variantsteps | journeystepsCompact (circle above label) or wide (dot beside index + label). Pick by available width.
orientationhorizontal | verticalhorizontalVertical stacks the steps down a side rail — for a narrow column or drawer.
active-stepint-1 (no derivation)When set, statuses are derived from the index (before it done, at it active, after it pending). A step's explicit status still wins.
labelstringProgressAccessible name for the nav element.
(app-step) labelstring-The step's label. Each <app-step> child is one step, in order.
(app-step) statusdone | active | pending | error-Overrides the status this step would otherwise derive from active-step.
(app-step) hrefstring-Makes the step a link back to that stage — only for a stage the user may revisit.
(app-step) descriptionstring-A secondary line under the label.
(app-step) iconstring-An icon name shown in the circle in place of the number (a done/error step keeps its own glyph).
onStepClick--React-only — makes each step a button reporting its index for a JS-driven wizard. No Razor counterpart; use href for server-rendered navigation instead.