Wizard

Organism
A modal workflow with a fixed step sidebar, a title and status header, and footer actions around a bounded, scrolling body. The application owns stage content, validation, saving and transitions; Wizard composes the existing Dialog, Steps and Button controls.

Playground

Every control sets a real prop of WizardProps. The JSX below updates as you go.
Props
title
string
current
number
1
status
string
JSX · WizardProps
<Wizard title="Create workspace" current={1} status="Step 2 of 4" />

Variants

1 examples from src/organisms/wizard/wizard.md, each rendered live from its own JSX.
Child owns scrolling
Set scrollBody={false} for a bounded child such as Chat that already manages its own thread scroll. The header and footer retain their positions.
<Stateful initial={false}>
{(open, setOpen) => <Column>
<Button outline onPress={() => setOpen(true)}>Open planning conversation</Button>
<ViewportOverlay><Wizard open={open} onOpenChange={setOpen} title="Plan a project"
steps={[{ label: "Details" }, { label: "Conversation" }, { label: "Review" }]} current={1}
scrollBody={false} status="Saved" actions={<Button primary onPress={() => setOpen(false)}>Done</Button>}>
<Chat messages={[{ id: "welcome", role: "assistant", content: "What should your project achieve?" }]} value="" onChangeText={() => {}} onSend={() => {}} />
</Wizard></ViewportOverlay>
</Column>}
</Stateful>

Anatomy

1
Panel
2
Steps
3
Body
4
Footer actions
Dimmed parts appear only in some states or variants.

Props

14 props on WizardProps, grouped as they are declared in source. Required props are marked *.
Prop
Type
Default
Description
Props
14 props
open *
boolean
false
Controlled modal visibility.
title *
string
–
Names the modal and its fixed header.
description
string
–
Supporting text below the title.
steps *
Step[]
–
Ordered stages; the application owns their content and transitions.
current *
number
–
Zero-based active stage; earlier stages read as complete.
onOpenChange *
(open: boolean) => void
–
Called when the close action, Escape or native back requests dismissal.
panelWidth
number
620
Maximum panel width; defaults to the hand-off's 620px and shrinks to its parent.
previous
{ label: string; onPress: () => void; disabled?: boolean }
–
Named previous stage action at the footer's start.
actions
ReactNode
–
Application actions at the footer's end.
status
string
–
Save/progress status beside the title in the fixed header.
children
ReactNode
–
scrollBody
boolean
true
Scroll the body (default true). Set false when a child, such as Chat, owns its scroll.
embedded
boolean
false
Show the open wizard as a non-modal specimen, for a catalogue or a showcase: it keeps its look, its dialog role and its labelling, but does not take focus, trap Tab, claim aria-modal, or close on Escape or native back. It renders in place, in the flow of the page, as Dialog's contained form does, rather than presenting into an OverlayProvider.
testID
string
–
Prefix for modal, steps, body-scroll and footer test hooks.

Accessibility and keyboard

Keyboard
Tab
Shift+Tab
Cycles focus inside the panel
Escape
Closes when dismissible
Enter
Confirms in forms
Semantics
No role of its own in source; it reads as the content it renders.
From the docs
Dialog supplies the accessible modal name, focus management, live close action, Escape and native back behavior. Embedded specimens keep the bare panel without a close action or modal focus ownership. The current sidebar stage carries aria-current="step". The previous action carries its stage name; application actions provide their own labels and disabled states. Footer buttons stay on one row; use Button shrink on application actions so long labels wrap within their button rather than moving the operation to a

Platform skins

The playground's props on each platform: same tree, native shapes, type and touch sizes.
Web
control 10 · field 10 · card 16 · Instrument Sans
iOS
capsule controls · field 8 · card 12 · SF Pro · 44 pt
Android
Material 3 stadium · field 4 · card 12 · Roboto · 48 dp

Usage

ViewportOverlay is docs-only scaffolding that presents the example above the docs page. In an application, mount Wizard beneath the app-root OverlayProvider.
<Stateful initial={false}>
{(open, setOpen) => <Stateful initial={0}>
{(current, setCurrent) => <Column>
<Button outline onPress={() => { setCurrent(0); setOpen(true); }}>Open project setup</Button>
<ViewportOverlay><Wizard open={open} onOpenChange={setOpen} title="Set up a project"
description="Choose the basics, then review your project."
steps={[{ label: "Details" }, { label: "Review" }]} current={current}
previous={current > 0 ? { label: "Details", onPress: () => setCurrent(0) } : undefined}
status={`Step ${current + 1} of 2`}
actions={<Button primary onPress={() => current === 0 ? setCurrent(1) : setOpen(false)}>{current === 0 ? "Review project" : "Create project"}</Button>}>
<Column roomy>
<Typography h3>{current === 0 ? "Project details" : "Ready to create"}</Typography>
{current === 0 ? <Input label="Project name" defaultValue="Customer portal" /> : <Typography body>Customer portal is ready to create.</Typography>}
</Column>
</Wizard></ViewportOverlay>
</Column>}
</Stateful>}
</Stateful>

Tokens

Read from the playground render. Values follow the scheme and the ion hue.
--bg2
--bls
--bw
--f-dp
--fd
--fm
--ft
--ion
--ion-fg
--ion-tx
--ln
--ln2
--sf
--sh-lg
--tx
--tx3

Related components