Skip to main content
WireKit
Copy for LLM

Stepper

The <x-wirekit::stepper> component shows progress through a multi-step flow. Each step is automatically styled based on its position relative to the current step: completed (filled), current (outlined), or upcoming (muted).

Usage

Horizontal Stepper
  1. Completed:
    Cart
  2. Shipping
  3. Payment
  4. Confirm

Steps before current show a checkmark. The current step is outlined with the accent color. Upcoming steps are muted.

With Descriptions

Stepper with Descriptions
  1. Completed:
    Cart
    Review items
  2. Completed:
    Shipping
    Address details
  3. Payment
    Card info
  4. Confirm
    Final review

Vertical Orientation

Vertical Stepper
  1. Completed:
    Account
    Create your account
  2. Profile
    Tell us about you
  3. Plan
    Choose a plan

Letting the reader step back

A stepper looks like the way back, and a finished step that does not answer a click costs the reader a click and a guess about the state of the page. Give the step a destination and it becomes one:

{{-- 1. `href` renders a real <a>; use `wire:click` instead for a Livewire action. --}}
{{-- 2. Only steps BEFORE `current` become operable — the rest stay presentational. --}}
<x-wirekit::stepper :current="3" :steps="[
    ['label' => 'Details', 'href' => route('wizard.details')],
    ['label' => 'Documents', 'href' => route('wizard.documents')],
    ['label' => 'Review'],
]" />

The first two carry a pointer and a focus ring; the third does not, and neither would a fourth. Hover the finished steps to see the difference:

Two steps done and clickable, the current one not

A step you did not give a destination stays presentational Being completed is not by itself a reason to be clickable. Your flow decides by supplying a target, so a stepper built from plain strings renders exactly as it always has — no tag, no class, no attribute added.

Props

Prop Type Default Description
steps array [] Array of step labels (strings) or arrays taking label, description, and — for a completed step — href or wire:click
current int 1 1-based index of the current step
orientation string 'horizontal' 'horizontal' or 'vertical'
scope string|null null Scoped personalization key

Accessibility

  • Outer <ol aria-label="Progress"> — semantic ordered list
  • Current step marked with aria-current="step" on its <li>
  • Completed steps include a visually hidden "Completed:" label for screen readers — the word runs through Laravel's __(), so it follows your app locale rather than staying English (see Localization)
  • Step numbers and icons are aria-hidden="true" (state conveyed via aria-current)
  • Connector lines are aria-hidden="true" (decorative)

Keyboard Interaction

By default the stepper is a presentational progress indicator — a semantic <ol> of steps with no focusable controls of its own. The current position is conveyed with aria-current, and the numbers, icons and connector lines are aria-hidden. Nothing to tab to, nothing to press.

Give a step an href or a wire:click and it becomes a real link or button — but only once it is completed (see "Letting the reader step back" above). A completed step is then an ordinary tab stop with a focus ring, and its accessible name is built from everything the element wraps — which is more than the label. The link covers the circle and the label block, so the name is the visually hidden "Completed:" prefix, then the label, then the description if the step has one. The first step in the preview under "Letting the reader step back" is announced as "Completed: Details Done — click to go back", not as "Details".

That is worth knowing before you write a description on a step you intend to make operable: it is part of that step's name, not a caption sitting beside it. Write it so it reads as one phrase with the label, or leave it off.

If your flow needs the user to move forward between steps, use <x-wirekit::wizard>: it holds the step, gates advancing on a per-step condition, announces the change to a screen reader, and renders this stepper inside itself. The stepper reflects state either way — it does not own the navigation, and the wizard does not change that.

Driving current from your own controls stays perfectly valid and is the right answer when the flow's state already lives somewhere else.

Pitfalls

  • A step becomes clickable only when you give it somewhere to go, and only once it is completed. The stepper shows where the reader is; it is not a nav, and it will not invent one. An upcoming step never becomes operable even with an href, because a jump forward is a permission only your flow can grant — a clickable skip in a checkout walks around the validation the steps exist for. <x-wirekit::wizard> keeps the same position for the forward direction: it adds Back and Next around the stepper and gates advancing.

Design Tokens

Token Used for
--font-wk-heading-weight Step number font weight
--text-wk-xs / --text-wk-sm Step number / label font size
--color-wk-text Active + completed step label
--color-wk-text-muted Pending step label
--color-wk-accent Active + completed circle background
--color-wk-accent-fg Active + completed circle text
--color-wk-bg Pending circle background
--color-wk-border Pending circle border + connector line
--border-wk-width Circle + connector border width
--padding-wk-x-sm / --padding-wk-y-xs / --padding-wk-y-md Step item padding
--transition-wk-duration State transition

Customization

Accent Color

The step indicator circles use --color-wk-accent for the current and completed states. Override it to match your brand:

Green Accent Stepper
  1. Completed:
    Cart
  2. Completed:
    Shipping
  3. Payment
  4. Confirm
Purple Accent Stepper
  1. Completed:
    Cart
  2. Shipping
  3. Payment
  4. Confirm

Completed Step at End

All Steps Completed
  1. Completed:
    Cart
  2. Completed:
    Shipping
  3. Completed:
    Payment
  4. Completed:
    Confirm

Vertical with Custom Color

Vertical Stepper — Blue Accent
  1. Completed:
    Order Placed
    April 1, 2026
  2. Completed:
    Processing
    April 2, 2026
  3. Shipped
    Estimated April 5
  4. Delivered
    Pending

Token Map by Element

Element Token
Circle completed bg --color-wk-accent
Circle completed text --color-wk-accent-fg
Circle current border --color-wk-accent
Circle current text --color-wk-accent
Circle upcoming border --color-wk-border
Circle upcoming text --color-wk-text-muted
Connector line --color-wk-border
Label text --color-wk-text
Description text --color-wk-text-muted
Circle size w-8 h-8 (2rem) — override via scoped personalization
Font weight (circle) --font-wk-heading-weight
Transition --transition-wk-duration

To change the circle size globally, use scoped personalization:

// config/wirekit.php
'personalizations' => [
    'stepper' => [
        'my-scope' => [
            'circle' => 'w-10 h-10 text-base', // larger circles
        ],
    ],
],
<x-wirekit::stepper scope="my-scope" :steps="['A', 'B', 'C']" :current="2" />

Config Defaults

The defaults live in config/wirekit.php under components.stepper. Override them globally:

'components' => [
    'stepper' => ['orientation' => 'vertical'], // default to vertical
],

Further Reading

Was this page helpful?

Voting requires cookies or local storage. What we store