StepForm

Create multi-step React forms with just a few lines of code.

The StepForm component is an abstraction around React Hook Form and follows the WAI specifications for forms.

Import#

  • StepForm: The wrapper component provides context, state, and focus management.
  • FormStepper: Renders a stepper that displays progress above the form.
  • FormStep: The form step containing fields for a specific step.
  • PrevButton: A button that this opens the previous step when clicked. Disabled on the first step.
  • NextButton: A button that submits the active step.
import {
StepForm,
FormStepper,
FormStep,
PrevButton,
NextButton,
} from '@saas-ui/react'

Usage#

Basic step form#

function BasicStepForm() {
const onSubmit = (params) => {
console.log(params)
return new Promise((resolve) => {
setTimeout(resolve, 1000)
})
}
return (
<StepForm
defaultValues={{
name: '',
email: '',
password: '',
}}
onSubmit={onSubmit}
>
<FormLayout>
<FormStep name="profile">
<FormLayout>
<Field name="name" label="Name" rules={{ required: true }} />
<Field name="email" label="Email" rules={{ required: true }} />
<NextButton />
</FormLayout>
</FormStep>
<FormStep name="password">
<FormLayout>
<Field
name="password"
label="Password"
rules={{ required: true, minLength: 4 }}
/>
<NextButton />
</FormLayout>
</FormStep>
</FormLayout>
</StepForm>
)
}

With a schema#

Supports Yup schemas out of the box.

function CreateProject() {
const schemas = {
project: Yup.object().shape({
name: Yup.string().required().label('Name'),
description: Yup.string().label('Description'),
}),
members: Yup.object().shape({
members: Yup.string().label('Members'),
}),
}
const onSubmit = (params) => {
console.log(params)
return new Promise((resolve) => {
setTimeout(resolve, 1000)
})
}
return (
<StepForm
defaultValues={{
name: '',
email: '',
password: '',
}}
onSubmit={onSubmit}
>
<FormLayout>
<FormStep name="project" resolver={yupResolver(schemas.project)}>
<FormLayout>
<Field name="name" isRequired label="Name" />
<Field name="description" label="Description" />
</FormLayout>
</FormStep>
<FormStep name="members" resolver={yupResolver(schemas.members)}>
<FormLayout>
<Field
name="members"
type="textarea"
label="Invite people"
placeholder="hello@saas-ui.dev, etc"
/>
</FormLayout>
</FormStep>
<ButtonGroup w="full">
<PrevButton variant="ghost" />
<Spacer />
<NextButton />
</ButtonGroup>
</FormLayout>
</StepForm>
)
}

With a stepper#

function CreateProject() {
const schemas = {
project: Yup.object().shape({
name: Yup.string().required().label('Name'),
description: Yup.string().label('Description'),
}),
members: Yup.object().shape({
members: Yup.string().label('Members'),
}),
}
const onSubmit = (params) => {
console.log(params)
return new Promise((resolve) => {
setTimeout(resolve, 1000)
})
}
return (
<StepForm
defaultValues={{
name: '',
email: '',
password: '',
}}
onSubmit={onSubmit}
>
<FormLayout>
<FormStepper>
<FormStep
name="project"
title="Project details"
resolver={yupResolver(schemas.project)}
>
<FormLayout>
<Field name="name" isRequired label="Name" />
<Field name="description" label="Description" />
</FormLayout>
</FormStep>
<FormStep
name="members"
title="Invite your team"
resolver={yupResolver(schemas.members)}
>
<FormLayout>
<Field
name="members"
type="textarea"
label="Invite people"
placeholder="hello@saas-ui.dev, etc"
autoFocus
/>
</FormLayout>
</FormStep>
<FormStep name="confirm" title="Confirm">
<FormLayout>
<Text>Please confirm that your information is correct.</Text>
<PropertyList>
<Property label="Name" value={<FormValue name="name" />} />
<Property
label="Description"
value={<FormValue name="description" />}
/>
</PropertyList>
</FormLayout>
</FormStep>
<StepperCompleted>
<Loader>We are setting up your project, just a moment...</Loader>
</StepperCompleted>
</FormStepper>
<ButtonGroup w="full">
<PrevButton variant="ghost" />
<Spacer />
<NextButton />
</ButtonGroup>
</FormLayout>
</StepForm>
)
}

With a vertical stepper#

function CreateProject() {
const schemas = {
project: Yup.object().shape({
name: Yup.string().required().label('Name'),
description: Yup.string().label('Description'),
}),
members: Yup.object().shape({
members: Yup.string().label('Members'),
}),
}
const onSubmit = (params) => {
console.log(params)
return new Promise((resolve) => {
setTimeout(resolve, 1000)
})
}
return (
<StepForm
defaultValues={{
name: '',
email: '',
password: '',
}}
onSubmit={onSubmit}
>
<FormLayout>
<FormStepper orientation="vertical">
<FormStep
name="project"
title="Project details"
resolver={yupResolver(schemas.project)}
>
<FormLayout>
<Field name="name" isRequired label="Name" />
<Field name="description" label="Description" />
<NextButton />
</FormLayout>
</FormStep>
<FormStep
name="members"
title="Invite your team"
resolver={yupResolver(schemas.members)}
>
<FormLayout>
<Field
name="members"
type="textarea"
label="Invite people"
placeholder="hello@saas-ui.dev, etc"
autoFocus
/>
<ButtonGroup>
<NextButton />
<PrevButton variant="ghost" />
</ButtonGroup>
</FormLayout>
</FormStep>
<FormStep name="confirm" title="Confirm">
<FormLayout>
<Text>Please confirm that your information is correct.</Text>
<PropertyList>
<Property label="Name" value={<FormValue name="name" />} />
<Property
label="Description"
value={<FormValue name="description" />}
/>
</PropertyList>
<ButtonGroup>
<NextButton />
<PrevButton variant="ghost" />
</ButtonGroup>
</FormLayout>
</FormStep>
<StepperCompleted>
<Loader>We are setting up your project, just a moment...</Loader>
</StepperCompleted>
</FormStepper>
</FormLayout>
</StepForm>
)
}

Access stepper state#

You can access the form step state by using render props or the useStepFormContext hook.

function StepperState() {
return (
<StepForm
defaultValues={{
name: '',
email: '',
password: '',
}}
onSubmit={saveHandler}
>
{({ isFirstStep, isLastStep, isCompleted, nextStep, prevStep }) => (
<FormLayout>
<FormStep name="profile">
<FormLayout>
<Field name="name" label="Name" />
<Field name="email" label="Email" />
</FormLayout>
</FormStep>
<FormStep name="password">
<FormLayout>
<Field name="password" label="Password" />
</FormLayout>
</FormStep>
{isCompleted ? (
<Text>Completed</Text>
) : (
<ButtonGroup>
<PrevButton />
<NextButton />
</ButtonGroup>
)}
</FormLayout>
)}
</StepForm>
)
}

Accessibility#

The StepForm component wraps the children in a HTML <form> element.

Keyboard Interaction#

KeyAction
EnterSubmit the form

Props#

onSubmitrequired

Description

The submit handler.

Type
SubmitHandler<TFieldValues>

context

Type
any

criteriaMode

Type
CriteriaMode

defaultValues

Type
{ [x: string]: any; }

delayError

Type
number

formRef

Description

Ref on the HTMLFormElement.

Type
RefObject<HTMLFormElement>

isCompleted

Type
boolean

mode

Type
keyof ValidationMode

onChange

Description

Triggers when any of the field change.

Type
WatchObserver<TFieldValues>

onError

Description

Triggers when there are validation errors.

Type
SubmitErrorHandler<TFieldValues>

ref

Type
ForwardedRef<UseFormReturn<TFieldValues, any>>

resolver

Type
Resolver<TFieldValues, any>

reValidateMode

Type
"onBlur" | "onChange" | "onSubmit"

schema

Description

The form schema, currently supports Yup schema only.

Type
any

shouldFocusError

Type
boolean

shouldUnregister

Type
boolean

shouldUseNativeValidation

Type
boolean

step

Type
string | number

Was this helpful?