stepper-ui
v1.2.5
Published
A customizable stepper component library for React with TailwindCSS.
Maintainers
Readme
Stepper UI
stepper-ui is a React component library that provides a customizable and easy-to-use Stepper, ideal for multi-step forms or workflows. Built with React, TypeScript, and designed to integrate with TailwindCSS.
Features
- TypeScript - Full TypeScript support with detailed type definitions
- Validation - Built-in support for step validation (sync and async)
- Customizable - Fully customizable step icons, classes, and rendering
- Performant - Memoized components to prevent unnecessary re-renders
- Accessible - Keyboard navigation support
Installation
npm install stepper-ui
# or using pnpm
pnpm add stepper-uiPeer Dependencies:
npm install react react-dom clsx tailwind-mergeQuick Start
import { Stepper } from 'stepper-ui'
function App() {
return (
<Stepper
steps={[
{ name: 'Step 1', component: StepOneComponent },
{ name: 'Step 2', component: StepTwoComponent },
{ name: 'Step 3', component: StepThreeComponent }
]}
renderButtons={({ nextStep, backStep, step, totalSteps }) => (
<div className="flex justify-between">
<button onClick={backStep} disabled={step === 0}>
Previous
</button>
<button onClick={nextStep} disabled={step === totalSteps - 1}>
Next
</button>
</div>
)}
/>
)
}Basic Usage
import { Stepper } from 'stepper-ui'
import { Button } from '@your-ui/button'
import { ArrowLeftIcon, ArrowRightIcon, TaskAddIcon } from '@your-icons'
import { FormPersonData } from './FormPersonData'
import { FormVehicles } from './FormVehicles'
<Stepper
steps={[
{ name: 'General Information', component: FormPersonData },
{ name: 'Additional Information', component: FormVehicles }
]}
renderButtons={({ nextStep, backStep, step, totalSteps }) => (
<div className="flex justify-between">
<Button
color="primary"
radius="full"
startContent={<ArrowLeftIcon />}
onPress={backStep}
disabled={step === 0}
>
Previous
</Button>
<Button
color="primary"
radius="full"
className="cursor-pointer"
endContent={step + 1 === totalSteps ? <TaskAddIcon /> : <ArrowRightIcon />}
onPress={nextStep}
disabled={step === totalSteps - 1}
>
{step + 1 === totalSteps ? 'Save' : 'Next'}
</Button>
</div>
)}
/>With Validation
Simple Validation (Synchronous)
import { forwardRef, useImperativeHandle } from 'react'
import type { StepperContextProps, ValidateStep } from 'stepper-ui'
export const FormPersonalData = forwardRef<ValidateStep, StepperContextProps>(
(props, ref) => {
const [isValid, setIsValid] = useState(false)
useImperativeHandle(ref, () => ({
canContinue: () => isValid
}))
return (
<div>
<input
type="text"
onChange={(e) => setIsValid(e.target.value.length > 0)}
/>
</div>
)
}
)Async Validation (e.g., API Check)
import { forwardRef, useImperativeHandle } from 'react'
import type { StepperContextProps, ValidateStep } from 'stepper-ui'
export const CheckEmailStep = forwardRef<ValidateStep, StepperContextProps>(
(props, ref) => {
useImperativeHandle(ref, () => ({
canContinue: async () => {
const response = await fetch('/api/check-email', {
method: 'POST',
body: JSON.stringify({ email: props.email })
})
const data = await response.json()
return data.isAvailable
}
}))
return <div>Check email availability...</div>
}
)With Custom Step Icons
import { Stepper } from 'stepper-ui'
import { UserIcon, CogIcon, CheckCircleIcon } from '@heroicons/react'
<Stepper
steps={[
{ name: 'Account', component: AccountStep },
{ name: 'Settings', component: SettingsStep },
{ name: 'Confirm', component: ConfirmStep }
]}
renderStepIcon={(label, step, active, completed) => (
<div className={`w-8 h-8 rounded-full flex items-center justify-center ${
active ? 'bg-blue-500 text-white' : completed ? 'bg-green-500 text-white' : 'bg-gray-300'
}`}>
{completed ? <CheckCircleIcon className="w-5 h-5" /> : label.charAt(0)}
</div>
)}
renderButtons={({ nextStep, backStep }) => (
<div className="flex gap-4">
<button onClick={backStep}>Back</button>
<button onClick={nextStep}>Next</button>
</div>
)}
/>Direct Navigation
You can use navigateTo to jump to any step:
<Stepper
steps={stepsArray}
renderButtons={({ step, navigateTo, goToInitialStep, totalSteps }) => (
<div className="flex justify-between">
<button onClick={goToInitialStep}>Restart</button>
<button onClick={() => navigateTo(0)}>Go to Step 1</button>
<button onClick={() => navigateTo(1)}>Go to Step 2</button>
<button onClick={() => navigateTo(totalSteps - 1)}>Go to Last</button>
</div>
)}
/>Props
StepperProps
| Prop | Type | Description | Required |
| ------------------ | -------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------- | -------- |
| steps | StepComponentProps[] | Array of steps for the Stepper | Yes |
| renderButtons | (props: RenderButtonsProps) => ReactNode \| ReactNode[] | Function that receives navigation methods and renders buttons | Yes |
| wrapperClassName | string | Additional TailwindCSS classes for the Stepper container | No |
| renderStepIcon | (label:string, step: number, active: boolean, completed: boolean) => ReactNode | Function to render a custom icon for each step | No |
StepComponentProps
| Prop | Type | Description | Required |
| ----------- | ------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------ | -------- |
| name | string | Name of the step displayed in the Stepper | Yes |
| component | ComponentType<StepperContextProps & RefAttributes<ValidateStep>> | React component for the step (must use forwardRef if validation is needed) | Yes |
| icon | ReactNode | Optional icon displayed next to the step name | No |
RenderButtonsProps
| Prop | Type | Description |
| ------------ | --------------------- | ----------------------------------------- |
| step | number | Current step index (0-based) |
| nextStep | () => Promise<void> | Navigate to the next step |
| backStep | () => void | Navigate to the previous step |
| totalSteps | number | Total number of steps in the Stepper |
ValidateStep
| Method | Return | Description |
| ------------- | ----------------------------- | ------------------------------------------------------------------------------------------------- |
| canContinue | boolean \| Promise<boolean> | Determines if navigation to next step is allowed. Use Promise<boolean> for async validation |
StepperContextProps
| Prop | Type | Description |
| ----------------- | ----------------------- | ------------------------------------------------------------------------- |
| step | number | Current step index (0-based) |
| nextStep | () => Promise<void> | Navigate to the next step |
| backStep | () => void | Navigate to the previous step |
| navigateTo | (step: number) => void | Navigate directly to a specific step by index |
| goToInitialStep | () => void | Navigate back to the first step (step 0) |
TypeScript
All components and hooks are fully typed. The library exports:
import {
Stepper,
type StepperProps,
type RenderButtonsProps,
type StepComponentProps,
type StepperContextProps,
type ValidateStep
} from 'stepper-ui'Performance
The Stepper component is optimized for performance:
- Memoized components -
React.memoprevents unnecessary re-renders - Optimized hook -
useCallbackanduseMemofor stable references - Efficient rendering - Only the active step component is mounted
Migration Guide
v1.2.x to v1.3.x
The nextStep function now returns Promise<void>:
// Before
await nextStep()
// After (unchanged - still works)
await nextStep()Recommendations
- Your project should have TailwindCSS configured for styling
- Use
forwardRef<ValidateStep>on step components if you need validation - Use
renderStepIconfor custom step indicators - The Stepper is internally controlled - you only provide UI
License
MIT © DuarteBv
Support
If you find a bug, please open an issue with a minimal reproduction.
