Recipe: Multi-step form
Goal: one command needs more input than fits comfortably on a single screen. Split it into a wizard — named steps the user moves through — that still executes a single command at the end.
Use StepperCommandDialog
Section titled “Use StepperCommandDialog”StepperCommandDialog is CommandDialog with stages. You group fields into StepperPanels; it handles next/back navigation, per-step validation, and runs the command when the last step is confirmed:
import { useState } from 'react';import { Guid } from '@cratis/fundamentals';import { StepperCommandDialog, StepperPanel } from '@cratis/components/CommandDialog';import { DropdownField, InputTextField } from '@cratis/components/CommandForm';import { RegisterMember } from './RegisterMember'; // generated proxy
const tiers = [ { id: 'standard', name: 'Standard' }, { id: 'premium', name: 'Premium' },];
export const RegisterMemberWizard = () => { const [memberId] = useState(() => Guid.create()); return ( <StepperCommandDialog<RegisterMember> command={RegisterMember} title='Register member' initialValues={{ memberId }} > <StepperPanel header='Details'> <InputTextField<RegisterMember> value={(command) => command.name} title='Name' /> <InputTextField<RegisterMember> value={(command) => command.email} title='Email' type='email' /> </StepperPanel> <StepperPanel header='Membership'> <DropdownField<RegisterMember> value={(command) => command.tier} title='Tier' options={tiers} optionValue='id' optionLabel='name' /> </StepperPanel> </StepperCommandDialog> );};The example assumes a RegisterMember command with a required memberId and name, email, and tier properties. DropdownField needs optionValue and optionLabel to know which option property is stored on the command and which one is shown. Open the wizard with useDialog, exactly like a single-step CommandDialog.
- One command, many steps. Every field across every panel maps to the same command — the wizard is just how you collect it. The command still validates and executes once.
- Per-step validation. Next is disabled while a field on the current step shows a validation error, and a visited step with an error is marked invalid. A field shows no error until it has been validated (on blur by default), so an untouched required field doesn’t block Next — but on the last step the submit button is only rendered once the whole command is valid. Set
validateOnInitto show every error as soon as the wizard opens. - Required values that aren’t inputs, like
memberIdabove, still go throughinitialValues(notonBeforeExecute), same as a single-step dialog — see Building a form.
When to use a wizard vs. a plain dialog
Section titled “When to use a wizard vs. a plain dialog”Reach for a wizard when the input is genuinely staged or long enough that one screen would overwhelm. For three or four fields, a single CommandDialog is friendlier — don’t add steps for their own sake.
- Building a form — the single-step recipe and the field set
- StepperCommandDialog — modal navigation, validation, callbacks, cancellation, and busy state
- CommandStepper — execute the same kind of multi-step command inline