Wizard
Navigation & Chrome

Wizard

A multi-step flow the reader works through: configure the instance, connect the database, invite the team, review, go.

Component behavior

Wizard renders a multi-step flow with validation, conditional steps, completion state, and ordered navigation. Validation can be asynchronous. Revisiting an earlier step preserves later completion state.

Use cases
  • First-run license, instance, database, and identity configuration.
  • Create or import tasks that require multiple screens.
  • Conditional steps based on earlier answers.
Alternatives

Stepper: only step progress is required.

Tour: steps explain an existing interface.

SettingsModal: sections are independent and unordered.

Form + Field: the task fits on one form screen.

Props

WizardProps props
PropTypeDefaultDescription
stepsWizardStep<T>[]—The flow. Written inline is fine.
activeStepstring—Controlled step id. Pair with onStepChange.
defaultStepstring—Where an uncontrolled wizard starts, and where reset returns to.
onStepChange(id, step, meta) => void—Fires on every move. meta carries the index and the direction.
valuesT—Controlled answers. Pair with onValuesChange.
defaultValuesT—Initial values for uncontrolled state.
onValuesChange(values: T) => void—Fires on every setValue / setValues.
completedstring[]—Controlled completion state.
defaultCompletedstring[]—Steps already passed at first render.
onCompletedChange(ids: string[]) => void—Fires whenever a step is marked done or undone.
enforceStepOrderbooleantrueSteps must be passed in order. Off, every step is reachable at once.
validateOnNextbooleantrueRun the step's validate on the way out.
onFinish(values: T) => void | Promise<void>—The reader finished. A promise holds submitting until it settles; a rejection becomes the last step's error.
onCancel() => void—Adds the Cancel button. Without it there is none.
onValidationError(id, message) => void—Fires whenever a step fails, with the message the reader will see.
layout"rail" | "steps" | "bare""rail"Rail beside the pane, band above it, or neither.
titleReactNode—Heading above the step navigation.
subtitleReactNode—Secondary text below the title.
brandReactNode—Logo mark beside the title.
headerActionsReactNode—Right-aligned slot in the pane header.
navFooterReactNode—Content at the bottom of the step navigation.
footerContentReactNode—Extra content on the left of the footer row.
progressDisplay"bar" | "count" | "both" | "none""both"Whether the reader is told where they are with a bar, a count, or both.
navWidthnumber264Rail width in px.
contentWidthnumber—Maximum width of the step content, in pixels.
contentAlign"start" | "center"—Defaults to centered in the band layout, left-aligned in the rail.
borderedbooleantrueWhether to render a bordered frame around the flow.
elevationSurfaceElevation"flat"Surface elevation for the frame. Use "dialog" for a centered card.
showStepHeaderbooleantrueThe step's title and description above its body.
showBackButtonbooleantrueBack button in the footer, hidden on the first step.
showCancelButtonbooleantrueShows the Cancel button when onCancel is set.
showSkipButtonbooleantrueSkip button on steps marked optional.
keyboardShortcutsbooleantrue⌘ / Ctrl + Enter advances.
focusOnStepChangebooleantrueMoves focus to the pane heading on every step change, never on first render.
labelsPartial<WizardLabels>—Overrides the button and counter copy.
renderNav(api) => ReactNode—Replaces the rail contents or the band.
renderHeader(api) => ReactNode—Replaces the pane header.
renderContent(api) => ReactNode—Replaces the step body, error strip included.
renderFooter(api) => ReactNode—Replaces the action row.
childrenReactNode—Rendered under the step body, inside the scrolling pane.

Steps

WizardStep props
PropTypeDefaultDescription
idstring—Stable id. What activeStep, goTo and onStepChange speak in.
labelReactNode—Nav label, and the pane title unless title overrides it.
titleReactNode—Pane title, when it should differ from the nav label.
descriptionReactNode—Dim line under the pane title.
hintReactNode—Dim second line under the nav label.
iconReactNode—Leading nav glyph, in place of the step number.
groupstring—Uppercase heading above this item in the rail. Consecutive steps sharing one sit under it.
badgeReactNode—Content after the step label, such as a count or status indicator.
optionalboolean—The footer grows a Skip button and the nav says so.
contentReactNode | ((api) => ReactNode)—The step's body. A function receives the live api.
validate(values, api) => WizardValidation | Promise<…>—Runs before leaving forwards. false or a message keeps the reader here.
includeWhen(values: T) => boolean—Includes the step only when the predicate returns true.
canAdvanceboolean—Hard gate on Next, for a step whose control is the validation.
disabledboolean—Not reachable from the nav, even when everything before it is done.
nextLabelReactNode—Replaces the advance button's label on this step.
backLabelReactNode—Replaces Back on this step.
actionsReactNode—Replaces this step's footer action row.
hideFooterboolean—Hides the footer for this step.
onEnter(api) => void—Called when the step is entered. Use to reveal a target inside a closed container.
onLeave(api) => void—Runs as the step is left, in either direction.

Labels

WizardLabels props
PropTypeDefaultDescription
nextReactNode"Continue"Advance button.
backReactNode"Back"Previous button.
finishReactNode"Finish setup"Advance button on the last step.
cancelReactNode"Cancel"Leave-entirely button, shown only with onCancel.
skipReactNode"Skip"Skip button on an optional step.
optionalReactNode"Optional"Nav marker on a step the reader may pass.
skippedReactNode"Skipped"Nav marker on an optional step they did pass.
stepCount(index, total) => ReactNode"Step 2 of 6"The counter line.
progress(percent: number) => ReactNode"40% complete"The line beside the progress bar.
navstring"Setup steps"Accessible name of the nav landmark.
status(status: WizardStepStatus) => string—Accessible state text on each nav item.

The pieces

WizardNav renders a Stepper using wizard steps, statuses, and reachability. Remaining StepperProps are forwarded except for props managed by the wizard.

WizardNavProps props
PropTypeDefaultDescription
orientation"vertical" | "horizontal""vertical"Rail or band.
variant"circles" | "bars" | "dots" | "panels""panels" vertical, "circles" horizontalStepper's drawing. A rail is navigation, so its steps default to pressable tiles; a band above the pane is the numbered strip.
size"sm" | "md" | "lg""md"Sets marker size, font size, and spacing for the step navigation.
toneToneProp"primary"Accent for done and current markers and the filled run of line.
labelPlacement"inline" | "below" | "none""inline"Passed to the stepper. below only makes sense on a horizontal band.
showConnectorsbooleantrueThe line between step markers. It breaks at every group heading, so a grouped rail reads as blocks.
showNumbersbooleantrueNumbers in the markers of steps that are neither done, errored nor skipped.
showDetailsbooleantrueShows step hints and Optional or Skipped labels. When false, displays step labels only.
fillbooleantrueDistributes horizontal steps evenly. Below-label connectors require equal-width columns.
asElementType"nav"The root element. The nav is a landmark named by labels.nav.
renderItem(step: WizardStep, state) => ReactNode—Custom step content receiving the step and index, status, reachable, and select values. Retains navigation buttons and state styling.
renderMarker(step: Step, state: StepState) => ReactNode—Replaces the marker and retains the label column. Receives the mapped Stepper step.
WizardFooterProps props
PropTypeDefaultDescription
showBackButtonbooleantrueBack button, hidden on the first step.
showCancelButtonbooleantrueCancel button, only when the wizard was given an onCancel.
showSkipButtonbooleantrueSkip button, only on steps marked optional.
childrenReactNode—Extra content on the left of the row.
WizardSummaryProps props
PropTypeDefaultDescription
itemsWizardSummaryItem[]—{ label, value, stepId?, mono?, group? } per row. A row's group defaults to the group of the step named by stepId, so the review falls under the same headings the rail used.
editLabelReactNode"Edit"The jump-back link's label.
columns1 | 21Lays the groups out side by side once there is room.
flatboolean—Ignores group and renders one flat list.

Steps, values and branches

steps is required. Each step has an id and label. content accepts a node or a function receiving the current wizard API.

A stepTSX
const steps: WizardStep<Setup>[] = [
  {
    id: "instance",
    label: "Instance",
    group: "Get started", // uppercase heading in the rail
    hint: "Name and public URL", // second line under the label
    description: "Instance identification settings.",
    validate: (values) =>
      values.url.startsWith("https://") || "The base URL has to be https://.",
    content: (api) => (
      <Field label="Base URL" htmlFor="url">
        <Input
          id="url"
          value={api.values.url}
          onChange={(e) => api.setValue("url", e.currentTarget.value)}
        />
      </Field>
    ),
  },
];

Use defaultValues for internal answer state or values with onValuesChange for controlled state. setValue and setValues update either form.

Branches

includeWhen controls whether a step is included. Excluded steps do not appear, count toward progress, or block navigation. If the active step becomes excluded, selection moves to the nearest preceding visible step.

A branchTSX
{
  id: "connection",
  label: "Connection",
  includeWhen: (values) => values.engine !== "bundled",
  validate: (values) => values.tested || "Run Test connection first.",
  content: (api) => …,
}

Validation

validate returns true or undefined for success, false for failure without a message, or a string for failure with a message. Promises show a spinner until settled. Thrown errors fail validation. Error messages appear above the step and mark it as failed in navigation.

Examples

A setup flow

Seven steps, three groups, one optional, two that come and go. Pick Bundled SQLite and the Connection step leaves the flow; pick Local accounts and the Provider step does.

Wizard

A short flow

layout="steps" places step navigation above the content. The default layout places navigation beside the content.

Wizard (band)

Custom layout

useWizard provides the state machine without layout. WizardProvider exposes it to WizardNav, WizardFooter, and WizardSummary.

Wizard (custom chrome)

Keyboard & accessibility

  • The rail is a nav of buttons; the current step carries aria-current="step" and every item has visually-hidden state text (“Completed”, “Needs attention”).
  • Unreachable steps are disabled and excluded from Tab navigation.
  • Focus moves to the pane heading on every step change, never on first render.
  • ⌘ / Ctrl + Enter advances.
  • Each navigation item exposes data-state: current, complete, skipped, error, or upcoming.

Import

ImportTSX
import {
  Wizard,
  WizardNav,
  WizardFooter,
  WizardSummary,
  WizardProvider,
  useWizard,
} from "@solarius/onset";
import type { WizardStep, WizardApi } from "@solarius/onset";

<Wizard
  steps={steps}
  defaultValues={{ url: "", engine: "postgres" }}
  title="Set up Anchorpoint"
  onFinish={install}
/>;