Multistep Form Page
Overview
The multistep form (wizard) pattern helps people to successfully finish complex flows, usually related to configuration or setup, by splitting them into steps. They limit cognitive load thanks to functional or semantic grouping of information and steps, allow faster error recovery and allow for session interruptions by enabling people to save their progress and return easily to where they left off.
Note: To avoid large setup and extra libraries, this template is using window.location.hash
for step navigation . In Grafana, we use locationService
from @grafana/runtime
to handle the routing navigation.
Source code
import React, { useEffect } from 'react';
import { Stack } from '@grafana/ui';
import { Stepper } from '@site/src/components/templates/MultistepFormPage/Stepper';
import { Data, StepKey } from '@site/src/components/templates/MultistepFormPage/types';
import { Step } from '@site/src/components/templates/MultistepFormPage/Steps/Step';
import { useForm, FormProvider } from 'react-hook-form';
import { getValidationResults } from '@site/src/components/templates/MultistepFormPage/utils/validation';
interface MultistepFormPageProps {
steps: Array<{ id: StepKey; name: string }>;
getStepUrl: (id?: string | number) => string;
activeStep: StepKey;
setVisitedSteps: (steps: StepKey[]) => void;
visitedSteps: StepKey[];
}
export const defaultFormData: Data = {
name: '',
email: '',
message: '',
radio: 'option1',
text: '',
slider: 1,
};
export const MultistepFormPage = ({
steps,
getStepUrl,
activeStep,
setVisitedSteps,
visitedSteps,
}: MultistepFormPageProps) => {
const methods = useForm({ defaultValues: defaultFormData, mode: 'onBlur' });
useEffect(() => {
if (!visitedSteps.includes(activeStep)) {
setVisitedSteps([...visitedSteps, activeStep]);
}
}, [activeStep]);
return (
<Stack direction={'column'}>
<FormProvider {...methods}>
<Stepper
// Prevent the user from moving to the next step if there are form errors
onStepChange={(_, event) => {
if (!!Object.keys(methods.formState.errors).length) {
event?.preventDefault();
}
}}
activeStep={activeStep}
steps={steps}
validationResults={getValidationResults(methods.getValues())}
getNextUrl={getStepUrl}
visitedSteps={visitedSteps}
/>
<Step activeStep={activeStep} />
</FormProvider>
</Stack>
);
};
export default MultistepFormPage;
Usage
When to use
A wizard pattern should be used for interactions that:
- Are complex or long.
- Can be divided into logical steps.
- Need to be performed in a specific sequence.
- Have a clear beginning and end (start with
Add
, end withSave
). - Are related to completing a specific action, such as creating an object, configuring settings, etc.
When not to use
- For short forms.
- For forms that cannot be logically divided into steps.
- For product tours.