Stepper
A multi-step progress indicator for wizards and multi-part forms. Built using Box, Row, Column, Icon, and Text components.
1
AccountCreate your account
2
ProfileSet up your profile
3
CompleteReview and finish
Step 1: Enter your account details
Source
'use client';
import { Check } from 'lucide-react';
import { createContext, useContext, useState } from 'react';
import { Box, Column, Icon, Row, Text } from '@umami/react-zen';
const StepperContext = createContext({
currentStep: 0,
totalSteps: 0,
goToStep: () => {},
nextStep: () => {},
prevStep: () => {},
});
export function useStepper() {
return useContext(StepperContext);
}
export function Stepper({ defaultStep = 0, totalSteps, children, ...props }) {
const [currentStep, setCurrentStep] = useState(defaultStep);
const goToStep = (step) => {
if (step >= 0 && step < totalSteps) {
setCurrentStep(step);
}
};
const nextStep = () => goToStep(currentStep + 1);
const prevStep = () => goToStep(currentStep - 1);
return (
<StepperContext.Provider value={{ currentStep, totalSteps, goToStep, nextStep, prevStep }}>
<Box {...props}>{children}</Box>
</StepperContext.Provider>
);
}
export function StepperHeader({ children }) {
return (
<Row alignItems="center" justifyContent="space-between" marginBottom="6">
{children}
</Row>
);
}
export function Step({ step, title, description }) {
const { currentStep, totalSteps } = useStepper();
const isCompleted = currentStep > step;
const isActive = currentStep === step;
const isLast = step === totalSteps - 1;
return (
<Row alignItems="center" flexGrow={isLast ? '0' : '1'}>
<Row alignItems="center" gap="3">
<Row
width="8"
height="8"
borderRadius="full"
backgroundColor={isCompleted ? 'interactive' : isActive ? 'primary' : 'surface-raised'}
border={!isCompleted && !isActive}
borderColor="muted"
alignItems="center"
justifyContent="center"
flexShrink="0"
>
{isCompleted ? (
<Icon size="sm">
<Check />
</Icon>
) : (
<Text weight="medium" color={isActive ? 'inverted' : 'muted'}>
{step + 1}
</Text>
)}
</Row>
<Column gap="0">
<Text weight={isActive ? 'medium' : undefined}>{title}</Text>
{description && <Text color="muted">{description}</Text>}
</Column>
</Row>
{!isLast && (
<Box
flexGrow="1"
height="1px"
marginX="4"
backgroundColor={isCompleted ? 'primary' : 'muted'}
/>
)}
</Row>
);
}
export function StepContent({ step, children, ...props }) {
const { currentStep } = useStepper();
if (currentStep !== step) return null;
return <Box {...props}>{children}</Box>;
}Variations
Simple (no descriptions)
Cart
2
Shipping
3
Payment
4
Confirm
Completed state
DetailsPersonal information
AddressShipping address
3
ReviewConfirm order
Previous steps completed!