Yugen UI

Stepper

Stepper shows which step of a multi-step process people are on; Wizard pairs it with each step's content and Back / Next buttons.

Stepper is an ordered list (horizontal or vertical). The current step has aria-current="step", and the number / check mark / exclamation mark indicator always comes with hidden text (“completed”, “error”…), so the state doesn't rely on color. States are derived from activeStep or set explicitly with status. With onStepChange the steps become buttons with a single tab stop, arrow keys (reversed in RTL) and Home / End; linear blocks jumping to steps not yet reached. The connector is a flex element, so it reverses in RTL. The horizontal version collapses to “Step 2 of 4 · step title” plus a ProgressBar when its container is narrower than 32rem (container query). Wizard runs validate (sync or a Promise) before moving to the next step. If that takes longer than 300ms the button shows a Spinner (Button loading); errors show with role="alert" and the step switches to the error state.

Step 1 of 4 · Account

Step 1 of 4: Account
Try "taken@example.com" to see a server error.

Import

Install from npm, copy the source into your project with @yugen-ui/cli or the shadcn CLI, or import directly when working in this repo.

TSX
// npm install yugen-uiimport { Stepper, Step, Wizard, type WizardStep, type StepStatus } from "yugen-ui";

Variants

Each tile is a standalone example: hover to copy, or press Code to see the source.

Step 2 of 4 · Shipping

Horizontal, controlled, non-linear

activeStep + onStepChange: click any step, or use the arrow keys and then Enter. Narrow the window to see the collapsed version.

  1. Step 1: Order placed, completed8:12 AM · Oct 21
  2. Step 2: Payment, completedBank transfer confirmed
  3. Step 3: Packing, errorOut of stock at the main warehouse
  4. Step 4: Delivery, not started

Vertical, error state, content inside a step

No onStepChange, so it is display only. In the vertical version, a Step's children appear in line with its text.

Step 2 of 4 · Income

Collapsed in a narrow container

The 288px container is narrower than 32rem, so it shows “Step 2 of 4 · Income” plus a ProgressBar. Turn this off with responsive={false}.

Usage

Do
  • Keep to 3–6 steps with short titles (nouns); merge steps if the process is longer.
  • Check each step with validate and explain errors in text. Wizard shows the message and marks the step as failed.
  • Use linear when each step depends on the previous one (checkout, opening an account).
Don't
  • Don't use Stepper in place of Tabs for independent sections with no order.
  • Don't signal errors with color alone. Use status="error", which adds an exclamation mark and the hidden text “error”.
  • Don't clear entered data when people go back.

Props

Prop · TypeDefaultDescription
activeStep / defaultActiveStepnumber / numberDefault: — / 0Stepper: index of the current step, from 0 (controlled / uncontrolled).
onStepChange(index: number) => voidDefault: —When set, the steps are buttons (roving focus, arrow keys / Home / End).
orientation"horizontal" | "vertical"Default: "horizontal"Display direction.
linearbooleanDefault: false (Wizard: true)Only allows going back or to completed steps; other steps get aria-disabled.
responsivebooleanDefault: trueHorizontal + container < 32rem → “Step n of N · title” plus a ProgressBar.
Step.title / descriptionReactNodeDefault: —Step title and short description.
Step.status"complete" | "current" | "upcoming" | "error"Default: —Overrides the state derived from the step's position.
Step.optional / disabledbooleanDefault: —Shows “Optional” / prevents moving to the step.
Wizard.stepsWizardStep[]Default: —{ id?, title, description?, optional?, content, validate? }.
WizardStep.validate() => boolean | string | void | Promise<…>Default: —false or a string blocks the step and shows an error; a Promise puts the Next button in loading (after 300ms).
Wizard.activeStep / defaultActiveStep / onStepChangenumber / number / (index) => voidDefault: —Current step, controlled or uncontrolled.
Wizard.onFinish() => void | Promise<void>Default: —Called when the last step passes validate; a Promise puts the Finish button in loading.
Wizard.backLabel / nextLabel / finishLabelReactNodeDefault: "Back" / "Next" / "Finish"Wizard button labels.

Messages

Text built into the component: labels, aria-labels, screen reader announcements.

English by default. Change it with the messages prop of YugenProvider, by the namespace below; messagesVi has the Vietnamese set. See Interface language.

messages.stepper

Key · TypeDefault (English)messagesVi
labelstring

aria-label mặc định của danh sách bước.

Default (English)"Steps"messagesVi"Các bước"
step(index: number) => string

Tiền tố ẩn (sr-only) trước tiêu đề bước, theo sau là ":"; index đếm từ 1.

Default (English)(index) => `Step ${index}`messagesVi(index) => `Bước ${index}`
completestring

Trạng thái đọc sau tiêu đề bước (sr-only).

Default (English)"completed"messagesVi"đã hoàn thành"
currentstringDefault (English)"current step"messagesVi"bước hiện tại"
upcomingstringDefault (English)"not started"messagesVi"chưa thực hiện"
errorstringDefault (English)"error"messagesVi"có lỗi"
optionalstring

Nhãn của bước optional.

Default (English)"Optional"messagesVi"Tuỳ chọn"
progress(current: number, total: number) => string

Dòng tóm tắt khi Stepper ngang thu gọn ở màn hẹp; current đếm từ 1.

Default (English)(current, total) => `Step ${current} of ${total}`messagesVi(current, total) => `Bước ${current}/${total}`
progressLabel(current: number, total: number) => string

aria-label thanh tiến độ khi thu gọn.

Default (English)(current, total) => `Step ${current} of ${total}`messagesVi(current, total) => `Bước ${current} trên ${total}`

messages.wizard

Key · TypeDefault (English)messagesVi
backstringDefault (English)"Back"messagesVi"Quay lại"
nextstringDefault (English)"Next"messagesVi"Tiếp tục"
finishstringDefault (English)"Finish"messagesVi"Hoàn tất"
invalidstring

Lỗi khi validate trả false.

Default (English)"Check the information in this step."messagesVi"Vui lòng kiểm tra lại thông tin ở bước này."
validateFailedstring

Lỗi khi validate ném lỗi.

Default (English)"Couldn't validate this step. Try again."messagesVi"Không kiểm tra được bước này. Hãy thử lại."
panel(current: number, total: number) => string

Tên ẩn của vùng nội dung bước, theo sau là ": " và tiêu đề bước; current đếm từ 1.

Default (English)(current, total) => `Step ${current} of ${total}`messagesVi(current, total) => `Bước ${current}/${total}`