Introducing PrimeReact v11-alpha 🎉Discover Now

useStepper

A hook that manages stepper state, step navigation, and ARIA attributes.

Content I
basic-demo

Usage#

import { useStepper } from '@primereact/headless/stepper';
 
const stepper = useStepper({ defaultValue: '1' });
const { rootProps, state } = stepper;
 
return (
    <div {...rootProps}>
        <div {...stepper.getItemProps({ active: true })}></div>
        <div {...stepper.getStepProps({ active: true, disabled: false })}></div>
        <button {...stepper.getHeaderProps({ activeValue: '1', disabled: false })}></button>
        {stepper.isStepActive('1') && <div {...stepper.getPanelProps({ active: true, activeValue: '1' })}></div>}
    </div>
);

useStepper manages active step state and returns spread-ready prop objects for each DOM element — see Primitive for a component-based API.

Features#

  • Single-hook architecture with getter functions for per-element props (rootProps, getItemProps, getStepProps, getHeaderProps, getPanelProps)
  • Controlled and uncontrolled value management
  • Linear mode to restrict free navigation between steps
  • Exposes state, setActiveStep, isStepActive, and isStepDisabled for custom logic

Behavior#

Default Value#

Use defaultValue to set the initially active step.

const stepper = useStepper({ defaultValue: '1' });

Controlled#

Use value and onValueChange for full programmatic control. The hook never updates its own state in controlled mode.

const [value, setValue] = React.useState<string | number | null>('1');
const stepper = useStepper({
    value,
    onValueChange: (e) => setValue(e.value)
});

The onValueChange callback receives { value } where value is the new active step key.

Linear#

Set linear to restrict step navigation. When enabled, isStepDisabled() returns true for inactive steps and getHeaderProps sets tabIndex to -1 on disabled headers.

const stepper = useStepper({ linear: true, defaultValue: '1' });

Use setActiveStep to programmatically navigate between steps in linear mode (e.g., via Next/Back buttons).

Vertical Layout#

The hook itself is layout-agnostic. For a vertical stepper, use Stepper.Item to group each step header and panel together, with the separator and content nested inside the panel area.

<div {...rootProps}>
    <div {...stepper.getItemProps({ active })}>
        <div {...stepper.getStepProps({ active, disabled })}>
            <button {...stepper.getHeaderProps({ activeValue: '1', disabled })}>Step 1</button>
        </div>
        {stepper.isStepActive('1') && (
            <div {...stepper.getPanelProps({ active: true, activeValue: '1' })}>
                <span>Separator</span>
                <div>Panel content</div>
            </div>
        )}
    </div>
</div>

Using setActiveStep and isStepActive#

The hook exposes imperative methods for custom logic outside the standard header click flow.

const stepper = useStepper({ defaultValue: '1' });
 
// Programmatically navigate to a step
stepper.setActiveStep('3');
 
// Check if a step is active
if (stepper.isStepActive('2')) {
    // ...
}

Custom Styling with Data Attributes#

Every prop object includes data-scope="stepper" and a data-part attribute. data-active is present only on active elements; data-disabled is added automatically when applicable.

[data-scope='stepper'][data-part='step'][data-active] {
    background-color: var(--primary);
}
 
[data-scope='stepper'][data-part='header'][disabled] {
    opacity: 0.5;
    pointer-events: none;
}
 
[data-scope='stepper'][data-part='panel'][data-active] {
    display: block;
}

API#

useStepper#

NameTypeDefault
defaultValuestring | number—
Default value of the active step.
valuestring | number—
Value of the active step.
linearbooleanfalse
Whether the steps are clickable or not.
onValueChange(event: useStepperChangeEvent) => void—
Callback fired when the stepper's value changes.

Accessibility#

See Stepper Primitive for WAI-ARIA compliance details and keyboard support.