Stepper
BetaAccessible
If a form is divided into multiple pages or steps, the Form progress component helps the user to understand the progression and to navigate between form steps.
Note about multi-page forms!
This documentation page is about HDS multi-page stepper component. If you are looking for documentation about building multi-page forms, please refer to HDS multi-page form pattern documentation page.
Available technologies
React
- Usage
- Code
- Accessibility
- Customisation
Code
Code examples
Default
- React
Press Enter to start editing. Press Esc to stop editing.
import { StepState, Stepper, Button, IconArrowLeft, IconArrowRight } from 'hds-react';() => {const commonReducer = (stepsTotal) => (state, action) => {switch (action.type) {case 'completeStep': {const activeStepIndex = action.payload === stepsTotal - 1 ? stepsTotal - 1 : action.payload + 1;return {activeStepIndex,steps: state.steps.map((step, index) => {if (index === action.payload && index !== stepsTotal - 1) {// current one but not last onereturn {state: StepState.completed,label: step.label,};}if (index === action.payload + 1) {// next onereturn {state: StepState.available,label: step.label,};}return step;}),};}case 'setActive': {return {activeStepIndex: action.payload,steps: state.steps.map((step, index) => {if (index === action.payload) {return {state: StepState.available,label: step.label,};}return step;}),};}default:throw new Error();}};const reducer = commonReducer(5);const initialState = {activeStepIndex: 0,steps: [{label: 'Step 1',state: StepState.available,},{label: 'Step 2',state: StepState.disabled,},{label: 'Step 3',state: StepState.disabled,},{label: 'Step 4 - longer text',state: StepState.disabled,},{label: 'Step 5',state: StepState.disabled,},],};const [state, dispatch] = useReducer(reducer, initialState);const lastStep = state.activeStepIndex === state.steps.length - 1;return (<div><Steppersteps={state.steps}language="en"selectedStep={state.activeStepIndex}onStepClick={(event, stepIndex) => dispatch({ type: 'setActive', payload: stepIndex })}/><divstyle={{display: 'flex',justifyContent: 'flex-start',alignItems: 'flex-end',gap: '24px',}}><Buttondisabled={state.activeStepIndex === 0}variant="secondary"onClick={() => dispatch({ type: 'setActive', payload: state.activeStepIndex - 1 })}style={{ height: 'fit-content', width: 'fit-content' }}iconStart={<IconArrowLeft />}>Previous</Button><Buttonvariant={lastStep ? 'primary' : 'secondary'}onClick={() => dispatch({ type: 'completeStep', payload: state.activeStepIndex })}style={{ height: 'fit-content', width: 'fit-content' }}iconEnd={lastStep ? undefined : <IconArrowRight />}>{lastStep ? 'Send' : 'Next'}</Button></div></div>);}
Small
- React
Press Enter to start editing. Press Esc to stop editing.
import { StepState, Stepper, Button, IconArrowLeft, IconArrowRight } from 'hds-react';() => {const commonReducer = (stepsTotal) => (state, action) => {switch (action.type) {case 'completeStep': {const activeStepIndex = action.payload === stepsTotal - 1 ? stepsTotal - 1 : action.payload + 1;return {activeStepIndex,steps: state.steps.map((step, index) => {if (index === action.payload && index !== stepsTotal - 1) {// current one but not last onereturn {state: StepState.completed,label: step.label,};}if (index === action.payload + 1) {// next onereturn {state: StepState.available,label: step.label,};}return step;}),};}case 'setActive': {return {activeStepIndex: action.payload,steps: state.steps.map((step, index) => {if (index === action.payload) {return {state: StepState.available,label: step.label,};}return step;}),};}default:throw new Error();}};const reducer = commonReducer(5);const initialState = {activeStepIndex: 0,steps: [{label: 'Step 1',state: StepState.available,},{label: 'Step 2',state: StepState.disabled,},{label: 'Step 3',state: StepState.disabled,},{label: 'Step 4 - longer text',state: StepState.disabled,},{label: 'Step 5',state: StepState.disabled,},],};const [state, dispatch] = useReducer(reducer, initialState);const lastStep = state.activeStepIndex === state.steps.length - 1;return (<divstyle={{maxWidth: '480px'}}><Steppersteps={state.steps}language="en"smallselectedStep={state.activeStepIndex}onStepClick={(event, stepIndex) => dispatch({ type: 'setActive', payload: stepIndex })}/><divstyle={{height: '80px',display: 'flex',justifyContent: 'flex-start',alignItems: 'flex-end',gap: '24px',}}><Buttondisabled={state.activeStepIndex === 0}variant="secondary"onClick={() => dispatch({ type: 'setActive', payload: state.activeStepIndex - 1 })}style={{ height: 'fit-content', width: 'fit-content' }}iconStart={<IconArrowLeft />}>Previous</Button><Buttonvariant={lastStep ? 'primary' : 'secondary'}onClick={() => dispatch({ type: 'completeStep', payload: state.activeStepIndex })}style={{ height: 'fit-content', width: 'fit-content' }}iconEnd={lastStep ? undefined : <IconArrowRight />}>{lastStep ? 'Send' : 'Next'}</Button></div></div>);}
With a step heading
- React
Press Enter to start editing. Press Esc to stop editing.
import { StepState, Stepper, Button, IconArrowLeft, IconArrowRight } from 'hds-react';() => {const commonReducer = (stepsTotal) => (state, action) => {switch (action.type) {case 'completeStep': {const activeStepIndex = action.payload === stepsTotal - 1 ? stepsTotal - 1 : action.payload + 1;return {activeStepIndex,steps: state.steps.map((step, index) => {if (index === action.payload && index !== stepsTotal - 1) {// current one but not last onereturn {state: StepState.completed,label: step.label,};}if (index === action.payload + 1) {// next onereturn {state: StepState.available,label: step.label,};}return step;}),};}case 'setActive': {return {activeStepIndex: action.payload,steps: state.steps.map((step, index) => {if (index === action.payload) {return {state: StepState.available,label: step.label,};}return step;}),};}default:throw new Error();}};const reducer = commonReducer(5);const initialState = {activeStepIndex: 0,steps: [{label: 'Step 1',state: StepState.available,},{label: 'Step 2',state: StepState.disabled,},{label: 'Step 3',state: StepState.disabled,},{label: 'Step 4 - longer text',state: StepState.disabled,},{label: 'Step 5',state: StepState.disabled,},],};const [state, dispatch] = useReducer(reducer, initialState);const lastStep = state.activeStepIndex === state.steps.length - 1;return (<div><Steppersteps={state.steps}language="en"stepHeadingselectedStep={state.activeStepIndex}onStepClick={(event, stepIndex) => dispatch({ type: 'setActive', payload: stepIndex })}/><divstyle={{height: '80px',display: 'flex',justifyContent: 'flex-start',alignItems: 'flex-end',gap: '24px',}}><Buttondisabled={state.activeStepIndex === 0}variant="secondary"onClick={() => dispatch({ type: 'setActive', payload: state.activeStepIndex - 1 })}style={{ height: 'fit-content', width: 'fit-content' }}iconStart={<IconArrowLeft />}>Previous</Button><Buttonvariant={lastStep ? 'primary' : 'secondary'}onClick={() => dispatch({ type: 'completeStep', payload: state.activeStepIndex })}style={{ height: 'fit-content', width: 'fit-content' }}iconEnd={lastStep ? undefined : <IconArrowRight />}>{lastStep ? 'Send' : 'Next'}</Button></div></div>);}
Overflow
- React
Press Enter to start editing. Press Esc to stop editing.
import { StepState, Stepper, Button, IconArrowLeft, IconArrowRight } from 'hds-react';() => {const commonReducer = (stepsTotal) => (state, action) => {switch (action.type) {case 'completeStep': {const activeStepIndex = action.payload === stepsTotal - 1 ? stepsTotal - 1 : action.payload + 1;return {activeStepIndex,steps: state.steps.map((step, index) => {if (index === action.payload && index !== stepsTotal - 1) {// current one but not last onereturn {state: StepState.completed,label: step.label,};}if (index === action.payload + 1) {// next onereturn {state: StepState.available,label: step.label,};}return step;}),};}case 'setActive': {return {activeStepIndex: action.payload,steps: state.steps.map((step, index) => {if (index === action.payload) {return {state: StepState.available,label: step.label,};}return step;}),};}default:throw new Error();}};const reducer = commonReducer(5);const initialState = {activeStepIndex: 0,steps: [{label: 'Step 1',state: StepState.available,},{label: 'Step 2',state: StepState.disabled,},{label: 'Step 3',state: StepState.disabled,},{label: 'Step 4 - longer text',state: StepState.disabled,},{label: 'Step 5',state: StepState.disabled,},],};const [state, dispatch] = useReducer(reducer, initialState);const lastStep = state.activeStepIndex === state.steps.length - 1;return (<divstyle={{maxWidth: '400px'}}><Steppersteps={state.steps}language="en"selectedStep={state.activeStepIndex}onStepClick={(event, stepIndex) => dispatch({ type: 'setActive', payload: stepIndex })}/><divstyle={{display: 'flex',justifyContent: 'flex-start',alignItems: 'flex-end',gap: '24px',}}><Buttondisabled={state.activeStepIndex === 0}variant="secondary"onClick={() => dispatch({ type: 'setActive', payload: state.activeStepIndex - 1 })}style={{ height: 'fit-content', width: 'fit-content' }}iconStart={<IconArrowLeft />}>Previous</Button><Buttonvariant={lastStep ? 'primary' : 'secondary'}onClick={() => dispatch({ type: 'completeStep', payload: state.activeStepIndex })}style={{ height: 'fit-content', width: 'fit-content' }}iconEnd={lastStep ? undefined : <IconArrowRight />}>{lastStep ? 'Send' : 'Next'}</Button></div></div>);}
Step states
- React
Press Enter to start editing. Press Esc to stop editing.
import { Stepper } from 'hds-react';() => (<Stepperlanguage="en"onStepClick={function noRefCheck(){}}selectedStep={1}steps={[{label: 'Completed',state: StepState.completed},{label: 'Current',state: StepState.available},{label: 'Available',state: StepState.available},{label: 'Needs attention',state: StepState.attention},{label: 'Paused',state: StepState.paused},{label: 'Disabled',state: StepState.disabled}]}/>)
Packages
| Package | Included | Storybook link | Source link |
|---|---|---|---|
| HDS React | Yes | View in Storybook | View source |
| HDS Core | No | - | - |
Properties
Note! You can find the full list of properties in the React Storybook.
| Property | Description | Values | Default value |
|---|---|---|---|
headingClassName | A custom class name for step heading. | string | - |
language | The language of the stepper. | "fi" "sv" "en" | "fi" |
onStepClick | A callback function for custom action on step click. | function | - |
selectedStep | The index of the selected step. | number | - |
small | If set to true, the small variant is used. | boolean | false |
stepHeading | If set to true, a step heading is shown below the stepper. | boolean | false |
stepHeadingAriaLevel | A step heading aria level. | number | 2 |
steps | The steps of the stepper. | Steps | - |
This component also accepts all nativediv element props.
Some native element props (className) are split into multiple children in the component.