Skip to Content

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!