Stepper

Display content divided into a steps sequence
Import

Usage

Step 2 content: Verify email
import { useState } from 'react';
import { Stepper, Button, Group } from '@mantine/core';
function Demo() {
const [active, setActive] = useState(1);
const nextStep = () => setActive((current) => (current < 3 ? current + 1 : current));
const prevStep = () => setActive((current) => (current > 0 ? current - 1 : current));
return (
<>
<Stepper active={active} onStepClick={setActive} breakpoint="sm">
<Stepper.Step label="First step" description="Create an account">
Step 1 content: Create an account
</Stepper.Step>
<Stepper.Step label="Second step" description="Verify email">
Step 2 content: Verify email
</Stepper.Step>
<Stepper.Step label="Final step" description="Get full access">
Step 3 content: Get full access
</Stepper.Step>
<Stepper.Completed>
Completed, click back button to get to previous step
</Stepper.Completed>
</Stepper>
<Group position="center" mt="xl">
<Button variant="default" onClick={prevStep}>Back</Button>
<Button onClick={nextStep}>Next step</Button>
</Group>
</>
);
}

Allow step select

To disable step selection set allowStepSelect prop on Stepper.Step component. It can be used to prevent user from reaching next steps while letting them go back and forth between steps they've already reached before:

Step 2 content: Verify email
import { useState } from 'react';
import { Stepper, Button, Group } from '@mantine/core';
function Demo() {
const [active, setActive] = useState(1);
const [highestStepVisited, setHighestStepVisited] = useState(active);
const handleStepChange = (nextStep: number) => {
const isOutOfBounds = nextStep > 3 || nextStep < 0;
if (isOutOfBounds) {
return;
}
setActive(nextStep);
setHighestStepVisited((hSC) => Math.max(hSC, nextStep));
};
// Allow the user to freely go back and forth between visited steps.
const shouldAllowSelectStep = (step: number) => highestStepVisited >= step && active !== step;
return (
<>
<Stepper active={active} onStepClick={setActive} breakpoint="sm">
<Stepper.Step
label="First step"
description="Create an account"
allowStepSelect={shouldAllowSelectStep(0)}
>
<Content>Step 1 content: Create an account</Content>
</Stepper.Step>
<Stepper.Step
label="Second step"
description="Verify email"
allowStepSelect={shouldAllowSelectStep(1)}
>
<Content>Step 2 content: Verify email</Content>
</Stepper.Step>
<Stepper.Step
label="Final step"
description="Get full access"
allowStepSelect={shouldAllowSelectStep(2)}
>
<Content>Step 3 content: Get full access</Content>
</Stepper.Step>
<Stepper.Completed>
<Content>Completed, click back button to get to previous step</Content>
</Stepper.Completed>
</Stepper>
<Group position="center" mt="xl">
<Button variant="default" onClick={() => handleStepChange(active - 1)}>
Back
</Button>
<Button onClick={() => handleStepChange(active + 1)}>Next step</Button>
</Group>
</>
);
}

Disable next steps selection

Another way to disable selection of upcoming steps is to use the allowNextStepsSelect directly on the Stepper component. This is useful when you don't need to control the behavior specifically for each step.

Step 2 content: Verify email
import { useState } from 'react';
import { Stepper, Button, Group } from '@mantine/core';
function Demo() {
const [active, setActive] = useState(1);
const nextStep = () => setActive((current) => (current < 3 ? current + 1 : current));
const prevStep = () => setActive((current) => (current > 0 ? current - 1 : current));
return (
<>
<Stepper active={active} onStepClick={setActive} breakpoint="sm" allowNextStepsSelect={false}>
<Stepper.Step label="First step" description="Create an account">
Step 1 content: Create an account
</Stepper.Step>
<Stepper.Step label="Second step" description="Verify email">
Step 2 content: Verify email
</Stepper.Step>
<Stepper.Step label="Final step" description="Get full access">
Step 3 content: Get full access
</Stepper.Step>
<Stepper.Completed>
Completed, click back button to get to previous step
</Stepper.Completed>
</Stepper>
<Group position="center" mt="xl">
<Button variant="default" onClick={prevStep}>Back</Button>
<Button onClick={nextStep}>Next step</Button>
</Group>
</>
);
}

Color, radius and size

You can use any color from theme.colors, by default Stepper will use theme.primaryColor:

Color
Radius
xs
sm
md
lg
xl
Size
xs
sm
md
lg
xl
import { Stepper } from '@mantine/core';
function Demo() {
return (
<Stepper size="md" active={1}>
<Stepper.Step label="Step 1" description="Create an account" />
<Stepper.Step label="Step 2" description="Verify email" />
</Stepper>
);
}

Component size is controlled by two props: size and iconSize. size prop controls icon size, label and description font size. iconSize allows to overwrite icon size separately from other size values:

import { Stepper } from '@mantine/core';
function Demo() {
return (
<Stepper iconSize={42} active={1}>
<Stepper.Step label="Step 1" description="Create an account" />
<Stepper.Step label="Step 2" description="Verify email" />
</Stepper>
);
}

With custom icons

You can replace step icon by setting icon prop on Step component. To change completed check icon set completedIcon on Stepper component. You can use any React node as icon: component, string, number:

import { useState } from 'react';
import { IconUserCheck, IconMailOpened, IconShieldCheck, IconCircleCheck } from '@tabler/icons-react';
import { Stepper } from '@mantine/core';
function Demo() {
const [active, setActive] = useState(1);
return (
<Stepper active={active} onStepClick={setActive} completedIcon={<IconCircleCheck />}>
<Stepper.Step icon={<IconUserCheck size="1.1rem" />} label="Step 1" description="Create an account" />
<Stepper.Step icon={<IconMailOpened size="1.1rem" />} label="Step 2" description="Verify email" />
<Stepper.Step icon={<IconShieldCheck size="1.1rem" />} label="Step 3" description="Get full access" />
</Stepper>
);
}

You can use Stepper with icons only. Note that in this case you will have to set aria-label or title on Step component to make it accessible:

import { useState } from 'react';
import { Stepper } from '@mantine/core';
import { IconUserCheck, IconMailOpened, IconShieldCheck } from '@tabler/icons-react';
function Demo() {
const [active, setActive] = useState(0);
return (
<Stepper active={active} onStepClick={setActive}>
<Stepper.Step icon={<IconUserCheck size="1.1rem" />} />
<Stepper.Step icon={<IconMailOpened size="1.1rem" />} />
<Stepper.Step icon={<IconShieldCheck size="1.1rem" />} />
</Stepper>
);
}

You can also change completed icon for each step, for example, to indicate error state:

import { Stepper } from '@mantine/core';
import { IconCircleX } from '@tabler/icons-react';
function Demo() {
return (
<Stepper active={2} breakpoint="sm">
<Stepper.Step label="Step 1" description="Create an account" />
<Stepper.Step
label="Step 2"
description="Verify email"
color="red"
completedIcon={<IconCircleX size="1.1rem" />}
/>
<Stepper.Step label="Step 3" description="Get full access" />
</Stepper>
);
}

Vertical orientation

import { useState } from 'react';
import { Stepper } from '@mantine/core';
function Demo() {
const [active, setActive] = useState(1);
return (
<Stepper active={active} onStepClick={setActive} orientation="vertical">
<Stepper.Step label="Step 1" description="Create an account" />
<Stepper.Step label="Step 2" description="Verify email" />
<Stepper.Step label="Step 3" description="Get full access" />
</Stepper>
);
}

Responsive styles

To change orientation based on viewport size set breakpoint prop. In following example stepper will have horizontal orientation when viewport width is more than theme.breakpoints.md:

<Stepper breakpoint="md">{/* ...steps */}</Stepper>

You can configure breakpoints with MantineProvider or use number to set breakpoint:

<Stepper breakpoint={755}>{/* ...steps */}</Stepper>

Icon position

To change step icon and body arrangement, set iconPosition to right, this option is most useful for RTL:

import { useState } from 'react';
import { Stepper } from '@mantine/core';
function Demo() {
const [active, setActive] = useState(1);
return (
<Stepper active={active} breakpoint="sm" onStepClick={setActive} iconPosition="right">
<Stepper.Step label="Step 1" description="Create an account" />
<Stepper.Step label="Step 2" description="Verify email" />
<Stepper.Step label="Step 3" description="Get full access" />
</Stepper>
);
}

Loading state

To indicate loading state set loading prop on Step component, Loader will replace step icon. You can configure default loader in MantineProvider.

import { Stepper } from '@mantine/core';
function Demo() {
return (
<Stepper active={1} breakpoint="sm">
<Stepper.Step label="Step 1" description="Create an account" />
<Stepper.Step label="Step 2" description="Verify email" loading />
<Stepper.Step label="Step 3" description="Get full access" />
</Stepper>
);
}

Customize with Styles API

You can change Stepper styles using Styles API

Get step ref

You can get refs of step button and stepper root element (div):

import { useRef } from 'react';
import { Stepper } from '@mantine/core';
function MyStepper() {
const firstStep = useRef<HTMLButtonElement>(null);
const stepper = useRef<HTMLDivElement>(null);
return (
<Stepper ref={stepper} active={0}>
<Stepper.Step label="First step" ref={firstStep} />
<Stepper.Step label="Second step" />
</Stepper>
);
}

Wrap Stepper.Step

Stepper component relies on Stepper.Step order. Wrapping Stepper.Step is not supported, Instead you will need to use different approaches:

import { Stepper } from '@mantine/core';
// This will not work, step children will not render
function WillNotWork() {
return (
<Stepper.Step label="Nope" description="It will not work">
This part will not render
</Stepper.Step>
);
}
// Create a separate component for children
function WillWork() {
return <div>This will work as expected!</div>;
}
function Demo() {
return (
<Stepper active={1}>
<Stepper.Step label="Regular step">First step</Stepper.Step>
{/* Wrapped Stepper.Step will not render children, do not do that */}
<WillNotWork />
<Stepper.Step label="Step with custom content">
<WillWork />
</Stepper.Step>
<Stepper.Step label="Regular step">Third step</Stepper.Step>
</Stepper>
);
}

Accessibility

<Stepper.Step /> components render button element, set aria-label or title props to make component visible for screen readers in case you do not specify label or description:

<Stepper.Step /> // -> not ok, empty labels for screen reader
<Stepper.Step label="Step 1" description="Create an account" /> // -> ok
<Stepper.Step aria-label="Create an account" /> // -> ok
<Stepper.Step title="Create an account" /> // -> ok

Stepper component props

NameTypeDescription
active *
number
Active step index
allowNextStepsSelect
boolean
Whether to enable click on upcoming steps by default. Defaults to true *
breakpoint
number | "xs" | "sm" | "md" | "lg" | "xl"
Breakpoint at which orientation will change from horizontal to vertical
children *
ReactNode
<Stepper.Step /> components only
color
MantineColor
Active and progress Step colors from theme.colors
completedIcon
ReactNode | StepFragmentComponent
Step icon displayed when step is completed
contentPadding
number | "xs" | "sm" | "md" | "lg" | "xl"
Key of theme.spacing or any valid CSS value to set content padding-top
icon
ReactNode | StepFragmentComponent
Step icon, defaults to step index + 1 when rendered within Stepper
iconPosition
"left" | "right"
Icon position relative to step body
iconSize
number
Step icon size
onStepClick
(stepIndex: number) => void
Called when step is clicked
orientation
"horizontal" | "vertical"
Component orientation
progressIcon
ReactNode | StepFragmentComponent
Step icon displayed when step is in progress
radius
number | "xs" | "sm" | "md" | "lg" | "xl"
Key of theme.radius or any valid CSS value to set border-radius, "xl" by default
size
"xs" | "sm" | "md" | "lg" | "xl"
Component size

Stepper.Step component props

NameTypeDescription
allowStepClick
boolean
Set to false to disable clicks on step
allowStepSelect
boolean
Should step selection be allowed
color
MantineColor
Step color from theme.colors
completedIcon
ReactNode | StepFragmentComponent
Step icon displayed when step is completed
description
ReactNode | StepFragmentComponent
Step description
icon
ReactNode | StepFragmentComponent
Step icon, defaults to step index + 1 when rendered within Stepper
iconPosition
"left" | "right"
Icon position relative to step body
iconSize
number
Icon wrapper size
label
ReactNode | StepFragmentComponent
Step label, render after icon
loading
boolean
Indicates loading state on step
orientation
"horizontal" | "vertical"
Component orientation
progressIcon
ReactNode | StepFragmentComponent
Step icon displayed when step is in progress
radius
number | "xs" | "sm" | "md" | "lg" | "xl"
Key of theme.radius or any valid CSS value to set border-radius, "xl" by default
size
"xs" | "sm" | "md" | "lg" | "xl"
Component size
state
"stepInactive" | "stepProgress" | "stepCompleted"
Step state, controlled by Steps component
step
number
Step index, controlled by Steps component *
withIcon
boolean
Should icon be displayed

Stepper component Styles API

NameStatic selectorDescription
root.mantine-Stepper-rootRoot element
steps.mantine-Stepper-stepsSteps controls wrapper
separator.mantine-Stepper-separatorSeparator line between step controls
verticalSeparator.mantine-Stepper-verticalSeparatorVertical separator line between step controls
separatorActive.mantine-Stepper-separatorActiveSeparator active modifier
verticalSeparatorActive.mantine-Stepper-verticalSeparatorActiveVertical separator active modifier
content.mantine-Stepper-contentCurrent step content wrapper
stepWrapper.mantine-Stepper-stepWrapperWrapper for the step icon and separator
step.mantine-Stepper-stepStep control button
stepIcon.mantine-Stepper-stepIconStep icon wrapper
stepCompletedIcon.mantine-Stepper-stepCompletedIconCompleted step icon, rendered within stepIcon
stepBody.mantine-Stepper-stepBodyContains stepLabel and stepDescription
stepLabel.mantine-Stepper-stepLabelStep label
stepDescription.mantine-Stepper-stepDescriptionStep description
stepLoader.mantine-Stepper-stepLoaderStep loader