React Tailwind CSS Vertical and Horizontal Stepper Component
This guide will show you how to build a modular and functional Vertical Stepper component in React using TypeScript and Tailwind CSS. A stepper is needed for Multi-Step Processes, for example in ecommerce checkout or in any kind of form completion.
Its better to use pre build functional stepper component with React and Tailwind, and save development time.
This guide covers both Vertical and Horizontal stepper components, designed with React and Tailwind CSS. With Tailwind’s utility-first approach, you can quickly style components without switching between files.
Follow along to integrate these ready-to-use steppers into your project.
Vertical Stepper Component
Here, we’ll implement each step in its own reusable Step component, and we’ll use a custom SVG icon to indicate completed steps. This modular structure helps you easily integrate and customize the component in your project.
Vertical Stepper Preview
Here’s how the Vertical Stepper component appears:
Code for Vertical Stepper Component
This stepper component is designed to be both reusable and dynamic. The main component (Stepper) renders each step and handles state, while the individual Step component controls the styling and behavior of each step.
Stepper Component
The Stepper component manages the steps' data and transitions between each step. By updating the step data, we can control which step is active and which steps are completed.
1const Stepper = () => { 2 const [steps, setSteps] = useState(stepsData) 3 4 const handleNextStep = () => { 5 setSteps((prevSteps) => { 6 const nextActiveIndex = prevSteps.findIndex((step) => step.active) + 1 7 8 return prevSteps.map((step, index) => { 9 return { 10 ...step, 11 active: index === nextActiveIndex, 12 completed: index < nextActiveIndex, 13 } 14 }) 15 }) 16 } 17 18 return ( 19 <> 20 <div className="flex max-w-md flex-col rounded-2xl bg-white p-10 font-[Inter] text-black"> 21 {steps.map((step, index) => ( 22 <Step key={index} step={step} isLast={index === steps.length - 1} /> 23 ))} 24 </div> 25 26 <button 27 onClick={handleNextStep} 28 disabled={steps[steps.length - 1].completed} 29 aria-label="Proceed to the Next Step" 30 className="rounded-md bg-white px-10 py-2 text-xl text-black transition-all duration-300 hover:scale-105 disabled:opacity-0" 31 > 32 {steps[steps.length - 1].active ? 'Done' : 'Next'} 33 </button> 34 </> 35 ) 36} 37 38export default Stepper
This component performs the following tasks:
- State Management: Uses a
useState
hook to manage the current step data, determining which step is active or completed. - Next Step Functionality: The
handleNextStep
function updates the active step when the "Next" button is clicked. - Button Control: The button disables when the last step is completed.
Step Component
Each Step component represents an individual step in the vertical stepper. It has a title, description, and a check mark for completed steps.
1import React from 'react' 2 3const CheckSVG = () => ( 4 <svg 5 width="13" 6 height="11" 7 viewBox="0 0 13 11" 8 fill="none" 9 xmlns="http://www.w3.org/2000/svg" 10 > 11 <path 12 fill-rule="evenodd" 13 clip-rule="evenodd" 14 d="M11.0964 0.390037L3.93638 7.30004L2.03638 5.27004C1.68638 4.94004 1.13638 4.92004 0.736381 5.20004C0.346381 5.49004 0.236381 6.00004 0.476381 6.41004L2.72638 10.07C2.94638 10.41 3.32638 10.62 3.75638 10.62C4.16638 10.62 4.55638 10.41 4.77638 10.07C5.13638 9.60004 12.0064 1.41004 12.0064 1.41004C12.9064 0.490037 11.8164 -0.319963 11.0964 0.380037V0.390037Z" 15 fill="white" 16 /> 17 </svg> 18) 19 20export interface StepType { 21 title: string 22 content: string 23 completed: boolean 24 active: boolean 25} 26 27type StepProps = { 28 step: StepType 29 isLast: boolean 30} 31 32const Step: React.FC<StepProps> = React.memo(({ step, isLast }) => { 33 const { title, content, active, completed } = step 34 35 return ( 36 <div className="flex gap-3"> 37 <div className="flex flex-col items-center"> 38 <div 39 className={`${active ? 'bg-gray-300' : 'bg-transparent'} rounded-full`} 40 > 41 {completed ? ( 42 <div className="m-1 flex size-6 items-center justify-center rounded-full bg-black"> 43 <CheckSVG /> 44 </div> 45 ) : ( 46 <div 47 className={`size-6 rounded-full border-8 ${active ? 'border-black' : 'border-gray-300'} m-1 transition-all duration-300`} 48 /> 49 )} 50 </div> 51 {!isLast && ( 52 <div 53 className={`border ${completed ? 'border-black' : 'border-gray-300'} h-full transition-all duration-300`} 54 /> 55 )} 56 </div> 57 <div> 58 <h3 className="mt-1 text-sm font-medium text-gray-800">{title}</h3> 59 <p className="mb-6 text-sm text-gray-500">{content}</p> 60 </div> 61 </div> 62 ) 63}) 64 65export default Step
Key parts of the Step
component include:
- Dynamic Styles: Uses Tailwind CSS classes to update styles dynamically based on the step's state (
active
orcompleted
). - Custom SVG Check Mark: Displays a custom SVG for completed steps, making it visually distinct.
- Conditional Line Display: Shows or hides the vertical line between steps based on the
isLast
prop, making the component modular.
Data Structure for Steps
1export interface StepType { 2 title: string 3 content: string 4 completed: boolean 5 active: boolean 6} 7 8export const stepsData: StepType[] = [ 9 { 10 title: 'Shipping Information', 11 content: 12 'Enter your shipping address to ensure prompt delivery of your items.', 13 completed: false, 14 active: true, 15 }, 16 { 17 title: 'Payment Details', 18 content: 19 'Provide your payment information securely to complete the purchase.', 20 completed: false, 21 active: false, 22 }, 23 { 24 title: 'Review Order', 25 content: 'Review the items in your cart and verify your order details.', 26 completed: false, 27 active: false, 28 }, 29 { 30 title: 'Confirmation', 31 content: 32 'Thank you for your purchase! Your order has been placed successfully.', 33 completed: false, 34 active: false, 35 }, 36]
This data defines each step's title, content, and state (whether it’s completed
or active
). You can customize this data to fit your specific process.
Using the Vertical Stepper Component in Your Project
Finally, here’s how to use the Stepper
component in your application:
1import Stepper from './components/VerticalStepper/Stepper' 2 3function App() { 4 return ( 5 <div className="flex min-h-dvh flex-col items-center justify-center gap-4 bg-gray-400 p-4 text-white"> 6 <Stepper /> 7 </div> 8 ) 9} 10 11export default App
The App
component renders the Stepper
component inside a flex container for central alignment.
Horizontal stepper component
Horizontal Stepper Preview
Tailwind CSS Horizontal Stepper Component Code
1import { useState } from 'react' 2 3interface Step { 4 label: string 5 step: number 6} 7 8interface StepperProps { 9 steps: Step[] 10} 11 12const Stepper: React.FC<StepperProps> = ({ steps }) => { 13 const [activeStep, setActiveStep] = useState(1) 14 15 const nextStep = () => { 16 setActiveStep((prevStep) => prevStep + 1) 17 } 18 19 const prevStep = () => { 20 setActiveStep((prevStep) => prevStep - 1) 21 } 22 23 const totalSteps = steps.length 24 25 const width = `${(100 / (totalSteps - 1)) * (activeStep - 1)}%` 26 27 return ( 28 <div className="mx-auto w-full max-w-2xl px-4 pb-10"> 29 <div className="before:transform-y-1/2 relative mt-14 flex justify-between before:absolute before:left-0 before:top-1/2 before:h-1 before:w-full before:bg-slate-200"> 30 {steps.map(({ step, label }) => ( 31 <div className="relative z-10" key={step}> 32 <div 33 className={`flex size-16 items-center justify-center rounded-full border-2 border-zinc-200 bg-white transition-all delay-200 ease-in ${ 34 activeStep >= step ? 'border-slate-400' : '' 35 }`} 36 > 37 {activeStep > step ? ( 38 <div className="rotate-45 -scale-x-100 text-2xl font-semibold text-slate-400"> 39 L 40 </div> 41 ) : ( 42 <span className="text-lg font-medium text-zinc-400"> 43 {step} 44 </span> 45 )} 46 </div> 47 <div className="absolute left-1/2 top-24 -translate-x-2/4 -translate-y-2/4"> 48 <span className="text-lg font-semibold text-zinc-400"> 49 {label} 50 </span> 51 </div> 52 </div> 53 ))} 54 <div 55 className="transform-y-1/2 absolute left-0 top-1/2 h-1 w-full bg-slate-400 transition-all delay-200 ease-in" 56 style={{ width: width }} 57 ></div> 58 </div> 59 <div className="mt-28 flex justify-between"> 60 <button 61 className="rounded-md border bg-gray-500 px-8 py-1.5 text-base font-medium text-white hover:bg-gray-600 disabled:cursor-not-allowed disabled:bg-gray-300 disabled:text-gray-700" 62 onClick={prevStep} 63 disabled={activeStep === 1} 64 > 65 Previous 66 </button> 67 <button 68 className="rounded-md border bg-gray-500 px-8 py-1.5 text-base font-medium text-white hover:bg-gray-600 disabled:cursor-not-allowed disabled:bg-gray-300 disabled:text-gray-700" 69 onClick={nextStep} 70 disabled={activeStep === totalSteps} 71 > 72 Next 73 </button> 74 </div> 75 </div> 76 ) 77} 78 79export default Stepper
Data Structure for Horizontal Stepper Component
To use this stepper component, you need to pass data to it as props
. It's helpful to keep this data separate, maybe in its own file, to keep the code organized.
1export const steps = [ 2 { 3 label: 'Address', 4 step: 1, 5 }, 6 { 7 label: 'Shipping', 8 step: 2, 9 }, 10 { 11 label: 'Payment', 12 step: 3, 13 }, 14 { 15 label: 'Summary', 16 step: 4, 17 }, 18]
How to use in Parent Component
And here's how you use it in a parent component
1import Stepper from './components/Stepper' 2 3function App() { 4 return ( 5 <> 6 <Stepper steps={steps} /> 7 </> 8 ) 9} 10 11export default App
These pre-built, fully responsive Vertical and Horizontal Stepper components in React and Tailwind CSS can simplify your project’s UI. By directly copying the provided code, you can implement efficient multi-step navigation in your app.
For feedback or suggestions, feel free to reach out. If this guide was helpful, share it with other developers!