Skip to main content

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.

Feature name

Further description of the feature and links to docs.
  1. General information
  2. Additional information
  3. Preview
1. General Information

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 with Save).
  • 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.

Figma designs (internal use only)