CommandStepper
The CommandStepper component executes one Arc command through an inline, multi-step form.
Purpose
Section titled “Purpose”Use CommandStepper when the wizard belongs directly in a page region, panel, or route. It establishes a CommandForm, renders StepperPanel steps with built-in navigation, and executes the command from the final step.
CommandStepper and StepperCommandDialog are sibling public components that share the private CommandStepperContent rendering primitive. Both execute the command. Choose StepperCommandDialog when the wizard should be modal; the dialog also owns dismissal.
Basic Usage
Section titled “Basic Usage”The example assumes CreateProject is a generated Arc command proxy whose Handle() returns a CreateProjectResponse, and that navigate comes from your router.
import { CommandStepper, StepperPanel } from '@cratis/components/CommandDialog';import { InputTextField, NumberField, TextAreaField } from '@cratis/components/CommandForm/fields';import { CreateProject } from '../api/projects/CreateProject';
type CreateProjectResponse = { projectId: string;};
export const ProjectWizard = () => ( <CommandStepper<CreateProject, CreateProjectResponse> command={CreateProject} onSuccess={(response) => navigate(`/projects/${response.projectId}`)} onFailed={(result) => console.error('Could not create project', result.exceptionMessages)} > <StepperPanel header='Basic info'> <InputTextField<CreateProject> value={(c) => c.name} title='Project name' /> <InputTextField<CreateProject> value={(c) => c.email} title='Contact email' type='email' /> </StepperPanel> <StepperPanel header='Details'> <TextAreaField<CreateProject> value={(c) => c.description} title='Description' /> <NumberField<CreateProject> value={(c) => c.budget} title='Budget' min={0} /> </StepperPanel> </CommandStepper>);CommandStepper is also exported from @cratis/components/CommandStepper; StepperPanel is only exported from @cratis/components/CommandDialog.
command: Command constructor used to establishCommandFormchildren:StepperPanelelementsnextLabel: Next button label. Falls back to theCratisComponentsProvider’smessages.stepper.next, then'Next'previousLabel: Previous button label. Falls back to the provider’smessages.stepper.previous, then'Previous'showNavigation: Show built-in navigation controls (default:true)onStepErrorsChange: Callback receiving a boolean array of per-step validation errorsshowSubmit: Show the built-in submit action on the last step (default:true)okLabel: Submit button label. Falls back to the provider’smessages.stepper.submit, then'Submit'isBusy: Disables the navigation controls while something is running- Other applicable
CommandFormprops, includinginitialValues,currentValues,validateOnInit, field-validation callbacks, and inherited command execution callbacks:onSuccess: Callback invoked with the typed response after successful command executiononValidationFailure: Callback invoked with validation results when command execution returns validation errorsonFailed: Callback invoked with the full command result for every unsuccessful result, including validation failures, as in the dialogsonException: Callback invoked with the exception messages and stack trace when the result has exceptionsonUnauthorized: Callback invoked when the user is not authorized to execute the command
onBeforeExecute: Transform command values before execution — it must return the values to run with, and it runs only on submit, so it can never satisfy required-field validation (seed those throughinitialValues). Submit turns busy before the transform runs, so an async transform cannot be submitted twiceconfirmBeforeExecute(values): Optional sync or async approval after validation andonBeforeExecute. Receives the transformed values; returnfalseto skip execution and remain on the last step. A rejected guard callsonException, notonFailed, and leaves Submit available to retrylinear(defaulttrue),orientation('horizontal'default /'vertical'),headerPosition('top'default /'bottom'),start,end, andpt: the activeStepperCustomizationPropssurface. It maps onto stableroot,list,step,header,number,title,separator,panels, andpanelparts.onChangeStep: Called once after each successful move to a different step through Previous, Next, or a clickable header. Receives{ index }, with a zero-based index. Blocked moves and clicks on the current header do not call it.ptOptionsandunstyled: retained temporarily for source compatibility; ignored because Cratis part attributes always merge and styling is CSS-owned.
Because CommandStepper has no outer dialog, it has no dialogPt or dialogUnstyled props; pt targets the stepper directly.
Conditional steps written as {condition && <StepperPanel/>} are counted correctly — only the panels that actually render are counted, so navigation and the per-step validation state stay in step with what is on screen. A <>…</> fragment wrapping several panels still counts as one step.
Navigation and Submit
Section titled “Navigation and Submit”- Previous is hidden on the first step and Next on the last step.
- Next is disabled while a field on the current step shows an error. With the default
validateOn='blur', an untouched blank field shows no error, so it does not block Next. PassvalidateOnInitto show errors from the start. linear(the default) makes other step headers unclickable; use Previous and Next to navigate. In non-linear mode, clickable headers also cannot advance past a current step showing an error. Neither mode requires a step to be complete before Next when no errors are shown.- The headers form an ordered list of buttons, not ARIA tabs. Each step panel is labelled by its header unless you pass
pt.header.id. In that case, the headers keep your id and the panels keep their text labels instead of referencing a shared header id. The current header hasaria-current="step". - On the last step Submit is always rendered, but it is disabled until the command passes client validation and no step shows an error. (
StepperCommandDialoghides its Submit button instead.) - While the transform, confirmation, or command runs, Next and Submit are disabled and Submit shows a spinner.
isBusydisables Previous, Next, and Submit for your own long-running work. - On failure the stepper stays on the last step, and server validation messages appear on their fields and turn their steps red.
- On success nothing else happens: the stepper keeps its values and stays on the last step. Navigate away or reset the surrounding view in
onSuccess.
Confirm before submitting
Section titled “Confirm before submitting”The same confirmBeforeExecute callback used by CommandDialog works on the inline stepper. Fields are disabled from the moment you submit until the guarded submission settles. If the values change during that time (for example, through currentValues), the command is not executed and no result callback runs; confirm again with the new values. Wrap the page in DialogComponents and call useConfirmationDialog from a child of that provider:
import { DialogButtons, DialogComponents, DialogResult, useConfirmationDialog } from '@cratis/arc.react/dialogs';import { CommandStepper, StepperPanel } from '@cratis/components/CommandDialog';import { InputTextField } from '@cratis/components/CommandForm/fields';import { ConfirmationDialog } from '@cratis/components/Dialogs';import { CreateProject } from './CreateProject';
function ProjectSteps() { const [confirm] = useConfirmationDialog('Create project?', 'Continue?', DialogButtons.YesNo); return ( <CommandStepper<CreateProject> command={CreateProject} confirmBeforeExecute={async (_values) => (await confirm()) === DialogResult.Yes}> <StepperPanel header='Details'> <InputTextField<CreateProject> value={(command) => command.name} title='Project name' /> </StepperPanel> </CommandStepper> );}
export function ProjectWizardWithConfirmation() { return <DialogComponents confirmation={ConfirmationDialog}><ProjectSteps /></DialogComponents>;}No keeps the stepper on the last step with its values intact. The type-checked dialog example is WithPreExecutionConfirmation in Source/CommandDialog/CommandDialog.stories.tsx.
Validation Indicators
Section titled “Validation Indicators”CommandStepper identifies CommandFormField children inside each StepperPanel and extracts the field names from their value accessors.
The step number circles are then styled based on state:
- Red: the step contains at least one field that currently shows an error
- Green: the step is visited and has no errors
- Default: not visited and no errors
See Also
Section titled “See Also”- StepperCommandDialog — place the same kind of command wizard in a modal dialog