Skip to content
WingoUI
ComponentsTemplatesDocsPricingAI agentsBlogChangelog
  1. Home
  2. Blog
  3. Component guides
  4. React Stepper Component: States, Variants and Phones
Auf Deutsch lesen
  1. Blog
  2. Component guides
  3. React Stepper Component: States, Variants and Phones
Component guides
Component guides

React Stepper Component: States, Variants and Phones

SR
Serban Rusu · Founder of Wingo UI
Oct 13, 2026 · 8 min read

On this page

0%
  1. When should you use a stepper?
  2. Which stepper variant fits which flow?
  3. What does each step state mean?
  4. Which steps should be clickable?
  5. How does a stepper collapse on a phone?
  6. How do you make a stepper accessible?
  7. What about a full checkout stepper in React?
  8. How does it compare with MUI and shadcn?
  9. Where should you start?
  10. FAQ
    1. What is a stepper component in React?
    2. Does shadcn/ui have a stepper component?
    3. How do I make a React stepper vertical?
    4. How should a stepper look on mobile?
    5. Should completed steps in a stepper be clickable?

TL;DR

A React stepper component shows where someone is in a fixed sequence: an ordered list where each step is complete, current, upcoming, failed or loading, with aria-current="step" on the current one. Use numbered circles for forms, bars for checkout, dots for onboarding and a vertical list when steps need descriptions. Below about 640px a horizontal stepper should collapse into a "Step 2 of 4" header with a progress bar, which is what the Wingo UI Steps component does in CSS.

Published Oct 13, 2026

A React stepper component answers one question at a glance: how far along am I, and what is left? Checkouts, onboarding flows, identity checks (KYC) and long settings forms all use one. The hard parts are the states (what does a failed or loading step look like?), the layout (five steps with titles do not fit a phone) and accessibility (a row of circles means nothing to a screen reader without the right markup). This guide covers each with the Steps component from Wingo UI, the library we build, and compares it with the main alternatives. If the stepper sits on top of a form, the React multi step form tutorial covers validation, saved drafts and transitions.

When should you use a stepper?

Use one when the task has a fixed order, three to seven steps, and a clear end. Each step should depend on the one before it, or at least read better in that order: shipping before payment, ID before proof of address.

Skip it in two cases. If the sections are independent and people jump between them freely, use Tabs; a stepper implies an order that is not there. If there is only one long step, a plain Progress bar is enough.

Which stepper variant fits which flow?

Numbered circles fit forms, bars fit checkout and dots fit onboarding. In Steps you pick one with variant:

VariantLooks likeGood for
circles (default)Numbered markers joined by connectors, titles under or beside themForms and wizards, account setup
barsOne segment per step with the title under itA checkout stepper, short flows with short titles
dotsSmall dots, the current one widenedOnboarding carousels, three to five slides

Two layout props change the rest. orientation="vertical" stacks the steps in a column with descriptions beside the markers. A vertical stepper fits KYC and account opening, where each step needs a line of explanation ("A bank statement or utility bill"), and it works at any width. labelPlacement="end" puts horizontal titles beside the markers instead of under them.

Then the visual props every Wingo UI component shares: size (sm, md, lg: 24, 28 or 32px markers), tone (neutral by default, or brand, success, warning, danger, info), color for any CSS color, and connector="dashed" to draw the connectors still ahead as dashes.

bash
npx wingo-ui@latest add steps
Click Continue and the connector fills toward the next step, the marker pops and the check draws in. Then click a completed step to go back to it. On a phone the same stepper shows the compact Step 2 of 4 header
$ npx wingo-ui@latest add steps
ProSteps docs

What does each step state mean?

Every step is in one of five states. Steps derives three from the current value: steps before it are complete, the one at it is current, the ones after it are upcoming. The other two are always explicit, set with status on the step:

StateMarkerScreen reader hearsSet by
completeA check that draws itself in"Completed"Position before value, or status
currentFilled with the tone, with a soft ring"Current step", plus aria-current="step"Position at value
upcomingThe number (or icon) on a light tintNothing extraPosition after value
errorA red "!""Needs attention"status: "error"
loadingA spinner"In progress"status: "loading"

Errors and loading are explicit because only your code knows them. A KYC flow is the clearest case: the identity check runs on a server, can take seconds, and can fail for a reason the person can fix.

tsx
"use client";
import { useState } from "react";
import { Steps, type StepItem } from "@/components/ui/steps";
import { Button } from "@/components/ui/button";
type Check = "idle" | "checking" | "failed" | "passed";
export function IdentityVerification({ verify }: { verify: () => Promise<boolean> }) {
const [check, setCheck] = useState<Check>("idle");
const steps: StepItem[] = [
{ id: "personal", title: "Personal details", description: "Emma Carter" },
{
id: "identity",
title: "Identity check",
description:
check === "failed" ? "The photo is blurry. Take it again." : check === "checking" ? "Checking your ID" : "Driver's license or passport",
status: check === "checking" ? "loading" : check === "failed" ? "error" : undefined,
},
{ id: "address", title: "Proof of address", description: "A bank statement or utility bill" },
{ id: "done", title: "Done" },
];
const send = async () => {
setCheck("checking");
setCheck((await verify().catch(() => false)) ? "passed" : "failed");
};
return (
<div className="flex flex-col gap-5">
<Steps steps={steps} value={check === "passed" ? "address" : "identity"} orientation="vertical" clickable="none" />
{check !== "passed" && (
<Button loading={check === "checking"} loadingText="Checking" onClick={send}>
{check === "failed" ? "Upload the photo again" : "Send the photo"}
</Button>
)}
</div>
);
}

Put the reason for an error in the step's description, as above. A red "!" tells people that something is wrong; the text tells them what to do. optional: true shows an "Optional" note under the title (dots, which have no visible title, add it for screen readers only), and disabled: true keeps a step from ever being clickable.

Which steps should be clickable?

clickable decides which steps are buttons. The default, "completed", makes completed steps, the current one and failed ones clickable, which covers the common "go back and fix the address" case without letting anyone skip ahead. "all" makes every enabled step a button, for flows where order is a suggestion. "none" turns the stepper into a pure indicator, as in the KYC example, where only the server moves you forward.

Clicks call onValueChange(id, step). A checkout stepper usually spans several routes, so the URL is the source of truth: give each step an href and pass your router's link. Steps renders clickable steps as links and the current one gets aria-current="step":

tsx
"use client";
import Link from "next/link";
import { usePathname } from "next/navigation";
import { Steps, type StepItem } from "@/components/ui/steps";
const CHECKOUT: StepItem[] = [
{ id: "cart", title: "Cart", href: "/checkout/cart" },
{ id: "shipping", title: "Shipping", href: "/checkout/shipping" },
{ id: "payment", title: "Payment", href: "/checkout/payment" },
{ id: "review", title: "Review", href: "/checkout/review" },
];
export function CheckoutProgress() {
const pathname = usePathname();
const current = CHECKOUT.find((step) => step.href === pathname)?.id ?? "cart";
return <Steps steps={CHECKOUT} value={current} variant="bars" linkComponent={Link} />;
}

With the default clickable, the upcoming steps stay plain text, so nobody lands on the payment page with an empty cart. Guard the routes on the server too; a stepper is not access control.

One pitfall: comparing usePathname() with href breaks as soon as the path carries a locale prefix or a trailing slash. If the stepper renders from app/checkout/layout.tsx, useSelectedLayoutSegment() from next/navigation returns "shipping" directly, and you can match it against the step id instead.

How does a stepper collapse on a phone?

Below 640px, a horizontal circles or bars stepper turns into a compact header: "Step 2 of 4" in small text, the current title, "Next: Payment" on the right and a thin progress bar under them. Both layouts are in the HTML and a CSS breakpoint picks one, so the server render is already right on a phone and nothing shifts after hydration. mobileVariant="same" keeps the full stepper on phones, and compact shows the compact header at every width, for a narrow card or a sheet.

Vertical steppers and dots keep their layout at every size, since both already fit a narrow screen. When a horizontal list is wider than its container and you opted out of the compact header, it scrolls sideways and keeps the current step in view.

On touch screens, clickable markers and dots get a 44px hit area, the minimum we use for every control. The reasoning is in our touch target size post.

How do you make a stepper accessible?

Use an ordered list, mark the current step with aria-current="step", put each step's state in words and support the arrow keys. In Steps the markup looks like this:

  • An ordered list. Steps renders an <ol> named "Progress" (labels.progress), so a screen reader announces a list of four items and the position of each.
  • aria-current="step" on the current step, the attribute value the ARIA spec defines for exactly this case.
  • Status in words. Each step's accessible name includes its state ("Shipping, Completed"), because color and a check icon are invisible to a screen reader.
  • Keyboard. Tab reaches each clickable step; Left and Right (Up and Down when vertical) move between them, Home and End jump to the ends.
  • The compact header is a progressbar with aria-valuenow and "Step 2 of 4" as its text value.

All of that copy is in labels, so a translation or a different tone of voice is one prop.

What about a full checkout stepper in React?

For a complete checkout instead of only the indicator, the Checkout Steps block puts four steps (delivery details, shipping, payment and review) beside a sticky order summary. It validates each step, shows lockers and pickup points, adds payment fees to the total, and turns each step into a full-height page on a phone with the total and Continue above the home indicator. Its header uses the Form Wizard stepper, "horizontal" or "progress".

Be aware of its defaults: it was built for the Romanian market, so country is "RO" (postal code and company tax ID checks), currency is "RON", and the Company switch asks for a CUI. Set country, currency and invoice={false} for other markets.

Pick a shipping method and press Continue: the stepper marks the step done and the summary total updates. On a narrow screen the summary folds into a bar at the top
$ npx wingo-ui@latest add checkout-steps
ProCheckout steps docs

How does it compare with MUI and shadcn?

MUI ships a mature stepper inside Material UI, shadcn/ui has none, and Wingo UI Steps is the one with a built-in phone header and error and loading states. The details, as of October 2026 (Material UI v9.5 docs):

Wingo UI StepsMUI Steppershadcn/ui
Installnpx wingo-ui@latest add steps@mui/material packageNo official stepper; community registries such as ReUI
Lookscircles, bars, dotsHorizontal and vertical steppers, plus a separate MobileStepper (text, dots, progress)Depends on the registry
Statescomplete, current, upcoming, error, loadingActive, completed, optional and an error exampleReUI: active, completed, inactive, loading
Phone layoutCompact header below 640px, automaticVertical stepper or MobileStepper, chosen by youUp to you
StylingTailwind v4 tokens, source in your repoMUI's styling systemTailwind, source in your repo

Sources: MUI Stepper docs (opens in a new tab), shadcn/ui component list (opens in a new tab), ReUI stepper docs (opens in a new tab).

MUI is the better pick if your app already uses Material UI: its stepper follows the rest of that design system, and its docs say vertical steppers "are designed for narrow screen sizes." If you want a shadcn stepper that installs with the shadcn CLI, ReUI's composed API (Stepper, StepperItem, StepperIndicator) fits that style. Wingo UI Steps suits a Tailwind stepper that should arrive with the states, the phone header and the keyboard support already built.

Where should you start?

Start with the Steps docs: the playground and examples for vertical steps with icons, a loading and error flow, bars, dots, linked steps and the phone header. Steps, Checkout Steps and Form Wizard are part of Wingo UI Pro: $8 a month, $80 a year or $150 once as of October 2026 (pricing). More guides like this one live in the component guides category, starting with the React form components guide.

Components in this post

  • Steps

    A React stepper for checkouts, onboarding and KYC: circles, bars or dots, with complete, error and loading states and a phone header.

    Pro
  • Checkout steps

    A React multi-step checkout: address and invoice, delivery with lockers, payment and a review that places the order, beside a summary.

    Pro

FAQ

What is a stepper component in React?

It is a progress indicator for a task split into ordered steps, such as a checkout or a sign-up. It shows which steps are done, which one is current and what is left, and it can let people jump back to a completed step.

Does shadcn/ui have a stepper component?

No. As of October 2026 the official shadcn/ui component list has no stepper. Community registries such as ReUI publish one that installs with the shadcn CLI, and you can also build one from an ordered list and Tailwind classes.

How do I make a React stepper vertical?

Stack the steps in a column with the markers on one side and the titles and descriptions beside them, and join the markers with a vertical connector. In Wingo UI Steps that is orientation="vertical", which also works at any screen width.

How should a stepper look on mobile?

A row of numbered circles with titles rarely fits a 360px screen. Show a compact header instead: the step count, the current title, what comes next and a progress bar, or switch to a vertical list when people need to see every step.

Should completed steps in a stepper be clickable?

Usually yes: going back to a completed step to fix something is the most common reason to click a stepper. Jumping forward past steps that were never filled in should stay blocked unless the steps are truly independent.

  • Stepper
  • Checkout
  • Onboarding
  • Accessibility
  • Mobile UI

Share

SR

About the author

Serban Rusu

Founder of Wingo UI

Serban Rusu founded Wingo UI. He builds its component library, CLI and MCP server, and writes about React interfaces for phones and AI coding agents.

More from Serban
Keep reading

Related posts

All posts
Component guides

React Multi Step Form with Zod, Saved Drafts and Motion

Build a React multi step form with per-step zod validation, an async check, progress saved across reloads and slides that follow direction, final code included.

SRSerban Rusu·Oct 13, 2026·12 min read
Build it: screens and apps

React Checkout Page: Cart, Steps, Coupons and a Pay Bar

Build a React checkout page from cart to confirmation: four checkout steps, coupons that recheck, a sticky pay bar on phones and form details that cut drop-off.

SRSerban Rusu·Oct 16, 2026·12 min read
Component guides

Embla Carousel React: Accessible Slides, No Layout Shift

Build an embla carousel react component for photos, listings, testimonials and onboarding, with keyboard controls, pausable autoplay and no layout shift.

SRSerban Rusu·Oct 19, 2026·9 min read
Newsletter

Get new posts by email

New guides, tutorials and comparisons from the Wingo UI blog, sent when they are published.

No spam. Unsubscribe at any time.

WingoUI

Animated, configurable, mobile-first React components and blocks. Copy the source, make it yours, and let your coding agent build with it.

Product

  • Components
  • Templates
  • Pricing
  • AI agents
  • Theme generator

Resources

  • Docs
  • Blog
  • Changelog
  • Changelog RSS
  • llms.txt

Legal

  • Terms
  • Privacy
  • Refunds
  • License
  • Imprint

Component categories

  • Buttons & Actions
  • Inputs
  • Forms
  • Navigation
  • Overlays
  • Feedback
  • Data Display
  • Tables & Lists
  • Charts & Stats
  • Layout
  • Media
  • AI Kit
  • Text & Effects
  • Mobile
  • Commerce
  • Marketing Sections
  • Blocks
  • Hooks & Utilities

© 2026 Wingo UI

Wingo UI