Layout & navigation

AppWizard

A multi-step flow with a progress header, one visible step at a time, and a sticky Back / Skip / Next / Finish footer.

<app-wizard><app-wizard-step label="…">…</app-wizard-step></app-wizard>

<app-wizard> is the whole flow, not just the progress header — AppStepper shows where the user is, but a wizard also owns which step's panel is visible and the footer that moves between them. Compose it with <app-wizard-step> children; each step's content is its panel.

The stepper header is built and driven the same way as AppStepper — steps / journey variant, horizontal / vertical orientation — so a form wizard and a standalone progress display never drift on look.

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 wizard above it.

Account panel.

Profile panel — optional, shows Skip.

Review panel.

<app-wizard>
  …
</app-wizard>

Basic

Three steps, one Next/Back footer. The second step is optional, so its footer shows a Skip button while it is active.

Three-step wizard
The wizard owns the active step on the client — no server round-trip between steps.

Account step content.

Profile step content — this one can be skipped.

Review step content.

Show code
<app-wizard>
    <app-wizard-step label="Account" description="Email &amp; password">
        <p>Account step content.</p>
    </app-wizard-step>
    <app-wizard-step label="Profile" description="Optional details" optional="true">
        <p>Profile step content — this one can be skipped.</p>
    </app-wizard-step>
    <app-wizard-step label="Review" description="Confirm and submit">
        <p>Review step content.</p>
    </app-wizard-step>
</app-wizard>

Journey variant

Give the wizard variant="journey" for the wide bar style — a large dot beside a "STEP N" index and the step name — the same trade-off as AppStepper's journey bar: pick it when there is roughly 900px of width to show every step without truncating.

variant="journey"
Same steps, same navigation — only the header layout changes.

Account step content.

Profile step content — this one can be skipped.

Review step content.

Show code
<app-wizard variant="journey">…</app-wizard>

Icons variant

Give the wizard variant="icons" for large connected icon badges with a "STEP N" eyebrow above each title. Like every variant it defaults to a single brand-primary accent — solid-primary active badge, soft-primary done badge, primary rail (no multi-colour). Give every step an icon — a step without one falls back to its number. This example starts on step 2 (active="1") so the done-rail and the solid active badge are both visible. For any other colour, see Colours.

variant="icons", six steps
Each step carries a Bootstrap Icons name; the badge shows a check once the step is done.

General information about the project.

Define the site and its structure.

Lay out the supply lines.

Configure the zone structure.

Run the analysis.

Review the result.

Show code
<app-wizard variant="icons" active="1">
    <app-wizard-step label="General Info" icon="info">…</app-wizard-step>
    <app-wizard-step label="Site &amp; Structure" icon="building">…</app-wizard-step>
    <app-wizard-step label="Supply Lines" icon="zap">…</app-wizard-step>
    <app-wizard-step label="Zone Structure" icon="layout-grid">…</app-wizard-step>
    <app-wizard-step label="Analysis" icon="chart-column">…</app-wizard-step>
    <app-wizard-step label="Result" icon="shield-check">…</app-wizard-step>
</app-wizard>

Colours

Every wizard is a single brand-primary accent by default — colour is opt-in, and it lives here. Add tone="primary | success | warning | danger | info" to recolour the whole accent (active fill, done rail, ring, done tint) to a semantic ramp, on any variant. For any other colour, override the --wui-step-accent / --wui-step-soft / --wui-step-soft-fg CSS variables in a style or class; resize the badges with --wui-step-size (compact) / --wui-step-badge-size (icons). Error steps always stay red.

tone presets
The default (no tone) is brand primary; these show success and danger. One prop recolours the whole stepper.

Primary — the default.

Second panel.

Third panel.

Success (green).

Second panel.

Third panel.

Danger (red).

Second panel.

Third panel.

Show code
<app-wizard>…</app-wizard>                  <!-- primary (default) -->
<app-wizard tone="success">…</app-wizard>
<app-wizard tone="danger">…</app-wizard>
Any custom colour — CSS variables
When no preset fits, set the --wui-step-* variables in a style (or product class). Here the icons wizard is recoloured to a custom purple.

General information.

Site and structure.

Supply lines.

Analysis.

Result.

Show code
<app-wizard variant="icons"
    style="--wui-step-accent: #7c3aed; --wui-step-soft: #efe7fd; --wui-step-soft-fg: #5b21b6;">
    …
</app-wizard>

Validation — gating Next with beforeNext

The wizard gates advancing on a validation check. On Razor, hook the element's cancelable wui-wizard:beforechange event and call preventDefault() to block Next, or Finish on the last step (its event carries reason: "finish"). The blocked step turns red and the wizard fires wui-wizard:invalid. Step status is derived from position, so going back from the last step correctly returns the steps ahead to their pending (grey) state rather than leaving them marked done.

Block Next until a field is valid
React uses the beforeNext prop; Razor wires the same gate through the element event. Here step 1 (Terms) is blocked until the box is ticked.

You must accept the terms before continuing.

Enter your payment details.

Review and submit.

Show code
<app-wizard id="gated-wizard">
    <app-wizard-step label="Terms">
        <input type="checkbox" id="gated-terms" /> I accept the terms
    </app-wizard-step>
    <app-wizard-step label="Payment">…</app-wizard-step>
    <app-wizard-step label="Confirm">…</app-wizard-step>
</app-wizard>

<script>
    document.getElementById('gated-wizard')
        .addEventListener('wui-wizard:beforechange', function (e) {
            if (e.detail.index === 0 && !document.getElementById('gated-terms').checked) {
                e.preventDefault(); // blocks Next; step turns red, wui-wizard:invalid fires
            }
        });
</script>
Async gate — the beforeNext JS propertyThe element also exposes a beforeNext property (the same one React sets from its prop) that can return a Promise — the wizard awaits it before advancing or flagging the step invalid.
beforeNext returning a Promise
The wizard awaits the returned Promise before advancing (or flagging the step invalid on false/rejection). Here it simulates a 600ms server-side terms check.

An async check (simulated 600ms request) must resolve true before Next advances.

Enter your payment details.

Review and submit.

Show code
<app-wizard id="async-gated-wizard">…</app-wizard>

<script>
    var wiz = document.getElementById('async-gated-wizard');
    wiz.beforeNext = function (index) {
        if (index !== 0) return true;
        return new Promise(function (resolve) {
            setTimeout(function () { resolve(termsChecked); }, 600);
        });
    };
</script>

Skip an optional step

A step marked optional shows a Skip button in the footer only while it is the active step — Skip is not a permanent extra button, it appears exactly where it applies and moves the wizard forward without requiring the panel's fields to validate.

optional: true → Skip
Only the Profile step is optional, so only its panel shows a Skip button. Skipping fires wui-wizard:skip with the step's index.

Account panel.

Profile panel — click Skip to move on without filling this in.

Review panel.

Show code
<app-wizard id="skip-wizard">
    <app-wizard-step label="Account">…</app-wizard-step>
    <app-wizard-step label="Profile" optional="true">…</app-wizard-step>
    <app-wizard-step label="Review">…</app-wizard-step>
</app-wizard>

<script>
    document.getElementById('skip-wizard')
        .addEventListener('wui-wizard:skip', function (e) {
            // e.detail.index = the skipped step
        });
</script>
Reserve optional for genuinely skippable stepsUse it for a step whose data can be filled in later (a profile photo, a secondary contact) — not for a step that is merely short. A skipped required step should fail validation on Finish, not silently pass.
skippable → Skip on every step
Set skippable on the wizard and every step (bar the last) shows a Skip button, without marking each one optional. Per-step optional still works on its own.

Any step can be skipped — Skip shows here without optional.

Second panel — also skippable.

Last step — no Skip, just Finish.

Show code
<app-wizard skippable="true">
    <app-wizard-step label="Account">…</app-wizard-step>
    <app-wizard-step label="Profile">…</app-wizard-step>
    <app-wizard-step label="Review">…</app-wizard-step>
</app-wizard>

Non-linear navigation

By default the stepper header only makes earlier (completed) steps clickable — forward movement is Next-only. non-linear="true" makes every step header clickable, so the user can jump straight to any step, forward or back.

non-linear="true"
Every step in the header is a button, including ones ahead of the current step. Starts on the second step so both directions are visible.

Account panel — every step header is clickable, not only earlier ones.

Profile panel.

Review panel — jump straight here from the header without using Next.

Show code
<app-wizard non-linear="true" active="1">
    <app-wizard-step label="Account">…</app-wizard-step>
    <app-wizard-step label="Profile">…</app-wizard-step>
    <app-wizard-step label="Review">…</app-wizard-step>
</app-wizard>

The Finish state

Pressing Finish on the last step fires wui-wizard:finish and moves the whole flow into its completed state: every step reads done (the connecting rail fills) and the active ring drops from the last step.

wui-wizard:finish
Starts on the last step — press Finish to see the header settle into its all-done state.

Set your email and password.

Tell us a little about yourself.

Review everything, then press Finish.

Show code
<app-wizard id="finish-wizard" active="2">…</app-wizard>

<script>
    document.getElementById('finish-wizard')
        .addEventListener('wui-wizard:finish', function () {
            // every step now reads done
        });
</script>

When to use

Use it when

  • A task is genuinely split across several ordered screens and each one is a real step, not just a tab.
  • You need the standard Back / Skip / Next / Finish footer wired up, not just a progress indicator.
  • Some steps are optional and the user should be able to skip past them explicitly.

Reach for something else when

  • It is one form on a single screen with grouped sections. → app-form-section + app-form-row + app-form-actions
  • You only need to show progress — navigation lives elsewhere (a route, a parent component). → app-stepper
  • The sections are alternative views the user switches between freely, not a sequence. → app-tabs

Every option

The whole surface of the component, one cell per value — the three header variants, every tone, both orientations, the forced step statuses, the standalone navigation flags, and custom footer labels.

variant — all three

variant="steps"

Account panel.

Profile panel.

Review panel.

variant="journey"

Account panel.

Profile panel.

Review panel.

variant="icons"

General Info panel.

Site & Structure panel.

Supply Lines panel.

tone — every preset, journey variant

tone="primary (default)"

Account panel.

Profile panel.

Review panel.

tone="primary"

Account panel.

Profile panel.

Review panel.

tone="success"

Account panel.

Profile panel.

Review panel.

tone="warning"

Account panel.

Profile panel.

Review panel.

tone="danger"

Account panel.

Profile panel.

Review panel.

tone="info"

Account panel.

Profile panel.

Review panel.

orientation

orientation="horizontal"

Account panel.

Profile panel.

Review panel.

orientation="vertical"

Account panel.

Profile panel.

Review panel.

step status — forced via a step's status

status="done"

Forced status panel.

Second panel.

status="active"

Forced status panel.

Second panel.

status="pending"

Forced status panel.

Second panel.

status="error"

Forced status panel.

Second panel.

standalone flags

skippable="true" — Skip on every non-last step

Account panel.

Profile panel.

Review panel.

non-linear="true" — every header step is clickable

Account panel.

Profile panel.

Review panel.

hide-stepper="true" — footer navigation only, no header

Account panel.

Profile panel.

Review panel.

disabled="true" — header and footer navigation both inert

Account panel.

Profile panel.

Review panel.

custom footer labels

back-label / next-label / finish-label

Account panel.

Profile panel.

Review panel.

skip-label — shown on the optional step

Account panel.

Profile panel — click "Later" to skip.

Review panel.

Attributes

AttributeTypeDefaultDescription
activeint0Initial step index. Only emitted on the element when greater than 0.
variantsteps | journey | iconsstepsThe stepper header style — compact circles, a wide journey bar, or large connected icon badges with a "STEP N" eyebrow.
toneprimary | success | warning | danger | info—Recolour the whole stepper accent to a semantic ramp. Unset defaults every variant to brand primary. For any other colour, override the --wui-step-accent / --wui-step-soft / --wui-step-soft-fg CSS variables (and --wui-step-size / --wui-step-badge-size for sizing).
skippableboolfalseShow a Skip button on every step (bar the last) without marking each optional. Per-step optional still works on its own.
orientationhorizontal | verticalhorizontalVertical stacks the header down a side rail — for a narrow column or drawer.
non-linearboolfalseAllow jumping to any step, not only completed ones.
hide-stepperboolfalseHide the progress header — footer navigation only.
disabledboolfalseDisable all navigation (header and footer).
labelstringProgressAccessible name for the stepper.
back-labelstringBackFooter's previous-step button label.
next-labelstringNextFooter's advance button label (hidden on the last step in favour of Finish).
skip-labelstringSkipFooter's skip button label — shown only while an optional step is active.
finish-labelstringFinishFooter's submit button label, shown on the last step.

<app-wizard-step> takes label, description (secondary line under the label), optional (shows Skip while active), icon (a glyph in the circle in place of the number) and status (done | active | pending | error — force a step's status instead of deriving it from the current step). Its child content is the step's panel.