Usage#
import { useStepper } from '@primereact/headless/stepper';const stepper = useStepper({ defaultValue: '1' });
const { rootProps, state } = stepper;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 — one call returns
rootPropsplus getter functions (getItemProps,getStepProps,getHeaderProps,getPanelProps) for every step part - Linear or free navigation — opt into
linearto gate forward jumps, or leave it off to let users bounce between steps - Controlled or uncontrolled — drive the active step from outside with
value/onValueChangeor let the hook own it - Layout-agnostic — no assumptions about horizontal vs vertical; compose panels and separators however you need
- Imperative controls —
setActiveStep()moves the pointer,isStepActive()andisStepDisabled()answer per-step queries
Working with callbacks#
Controlled active step#
Drive the active step from parent state using value and onValueChange.
const [value, setValue] = React.useState<string | number | null>('1');
const stepper = useStepper({
value,
onValueChange: (e) => setValue(e.value)
});
<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>;onValueChange receives { value } where value is the new active step key.
Wizard-style Next/Back navigation#
setActiveStep is the imperative escape hatch for Next/Back buttons — essential when linear mode prevents clicking headers directly.
const stepper = useStepper({ linear: true, defaultValue: '1' });
<button onClick={() => stepper.setActiveStep('2')}>Next</button>
<button onClick={() => stepper.setActiveStep('1')}>Back</button>Gating with linear mode#
Turn on linear to lock inactive steps — isStepDisabled() will return true for them and getHeaderProps sets tabIndex to -1, so only the active step is reachable by keyboard.
const stepper = useStepper({ linear: true, defaultValue: '1' });
stepper.isStepDisabled('3'); // true until user advancesVertical layout composition#
The hook stays layout-agnostic. For a vertical stepper, use getItemProps to wrap each header and panel together so separators can nest inside the panel region.
<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>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#
| Name | Type | Default |
|---|---|---|
defaultValue | string | number | — |
| Default value of the active step. | ||
value | string | number | — |
| Value of the active step. | ||
linear | boolean | false |
| Whether the steps are clickable or not. | ||
onValueChange | (event: useStepperChangeEvent) => void | — |
| Callback fired when the stepper's value changes. | ||
Accessibility#
Arrow keys navigate steps, Enter activates the focused step, and disabled steps are skipped. See Primitive for full WAI-ARIA compliance details.