Stepper
Stepper displays progress through a multi-step process, allowing users to navigate between steps and track their progress.
Publication Information
Enter the basic information about your publication
Usage
import { Stepper, StepperHeader, StepperContent, StepperFooter, useStepper, Button } from '@e-infra/design-system'
// Define your steps
const steps = [
{ label: "Step 1", description: "First step description" },
{ label: "Step 2", description: "Second step description" },
{ label: "Step 3", description: "Third step description" },
]
// Basic usage
<Stepper>
<StepperHeader steps={steps} />
<StepperContent>
<YourStep1Content />
<YourStep2Content />
<YourStep3Content />
</StepperContent>
</Stepper>
// Optional: custom footer actions
function StepperActions() {
const { currentStep, totalSteps, nextStep, previousStep } = useStepper();
const isLastStep = currentStep === totalSteps - 1;
return (
<StepperFooter showDefaultButtons={false}>
<Button variant="outline" onClick={previousStep} disabled={currentStep === 0}>
Back
</Button>
<Button onClick={isLastStep ? () => console.log("Done") : nextStep}>
{isLastStep ? "Submit" : "Continue"}
</Button>
</StepperFooter>
);
}Components
| Component | Description |
|---|---|
Stepper | Container component that manages step state and context |
StepperHeader | Displays progress indicator, current section label, and header navigation |
StepperContent | Displays content for the current step |
StepperFooter | Optional footer area for default or custom actions |
Props
Stepper
| Prop | Type | Default | Description |
|---|---|---|---|
| children | React.ReactNode | - | Step content and child components |
| initialStep | number | 0 | Starting step index (uncontrolled mode) |
| step | number | - | Controlled current step (0-based). When set, the parent owns the step and receives updates through onStepChange |
| totalSteps | number | - | Total number of steps. If omitted, it is inferred from Stepper direct children, so pass explicitly when your content count differs. |
| onStepChange | (step: number) => void | - | Callback when step changes |
StepperHeader
| Prop | Type | Default | Description |
|---|---|---|---|
| steps | Step[] | - | Array of step definitions with label, optional description, and icon |
| className | string | - | Additional CSS classes |
| showNavigation | boolean | true | Show the built-in Previous/Next buttons. Hide them when providing custom navigation |
StepperContent
| Prop | Type | Default | Description |
|---|---|---|---|
| children | React.ReactNode | - | Content for each step |
| className | string | - | Additional CSS classes |
StepperFooter
| Prop | Type | Default | Description |
|---|---|---|---|
| children | React.ReactNode | - | Custom footer content (overrides default buttons) |
| className | string | - | Additional CSS classes |
| showDefaultButtons | boolean | true | Show default Previous/Next/Finish buttons |
| nextLabel | string | "Next" | Label for Next button |
| previousLabel | string | "Previous" | Label for Previous button |
| finishLabel | string | "Finish" | Label for Finish button on last step |
| onFinish | () => void | - | Callback when Finish button is clicked |
Controlled vs Uncontrolled
The Stepper works as an uncontrolled component by default, managing its own internal state from initialStep. For full external control — e.g. when the step lives in a parent form, a store, or the URL — pass step together with onStepChange and hide the built-in header buttons in favor of your own navigation:
function ControlledStepper() {
const [currentStep, setCurrentStep] = React.useState(0);
return (
<Stepper
step={currentStep}
totalSteps={steps.length}
onStepChange={setCurrentStep}
>
<StepperHeader steps={steps} showNavigation={false} />
<StepperContent>{/* Your step content */}</StepperContent>
<StepperFooter showDefaultButtons={false}>
{/* Custom controls via useStepper() */}
</StepperFooter>
</Stepper>
);
}Accessibility
The Stepper component includes built-in accessibility features:
- ARIA labels on navigation buttons
- Keyboard navigation support via clickable step indicators
- Screen reader announcements for current step changes via a polite live region
- Semantic HTML structure with proper landmark roles
Examples
With Initial Step
Start the stepper at a specific step:
<Stepper initialStep={2}>
<StepperHeader steps={steps} />
<StepperContent>{/* Your step content */}</StepperContent>
<StepperFooter onFinish={() => console.log("Done")} />
</Stepper>Custom Footer
Provide custom navigation controls by using useStepper() inside a child component:
function CustomFooterActions() {
const { currentStep, totalSteps, nextStep, previousStep } = useStepper();
const isLastStep = currentStep === totalSteps - 1;
return (
<StepperFooter showDefaultButtons={false}>
<Button
variant="outline"
onClick={previousStep}
disabled={currentStep === 0}
>
Back
</Button>
<Button onClick={isLastStep ? () => submit() : nextStep}>
{isLastStep ? "Submit" : "Continue"}
</Button>
</StepperFooter>
);
}
<Stepper totalSteps={steps.length}>
<StepperHeader steps={steps} showNavigation={false} />
<StepperContent>{/* Your step content */}</StepperContent>
<CustomFooterActions />
</Stepper>;Step Icons
Show an icon in each step marker instead of the step number. Any component accepting an optional className works — the stepper passes marker sizing to it:
import { FileText, FileSearch, BookUser, CircleCheck } from "lucide-react";
const steps = [
{ label: "Publication Info", icon: FileText },
{ label: "Duplicity Check", icon: FileSearch },
{ label: "Authors", icon: BookUser },
{ label: "Finish", icon: CircleCheck },
];On Step Change Callback
Track or validate before step changes:
<Stepper
onStepChange={(step) => {
console.log(`Moving to step ${step}`);
// Perform validation or analytics here
}}
>
{/* Your stepper content */}
</Stepper>