How to create Stepper Component Using React JS ?
Last Updated :
24 Apr, 2025
The checkout process on e-commerce websites often involves a series of steps, commonly represented by a stepper component. In this tutorial, we will guide you through creating a customizable stepper component using React, allowing you to easily integrate it into your web applications.
Preview of final output: Let us have a look at how the final output will look like.
Project Preview
Prerequisites
Approach
Our approach will involve a configuration-driven UI, where we define the steps and their corresponding components in a configuration object. This allows for flexibility and easy customization. We will count each step and accordingly the completion graph will change and all passing through all the steps it will shows the completion of the stepper.
Steps to Create the React Application
Step 1: Set Up Your React App with Vite
npm create vite@latest

Step 2: Navigate to the Project Directory
cd stepper
Step 3: Install the project dependencies using:
npm install
Project Structure:
project structureDependencies:
"dependencies": {
"react": "^18.2.0",
"react-dom": "^18.2.0"
},
"devDependencies": {
"@types/react": "^18.2.55",
"@types/react-dom": "^18.2.19",
"@vitejs/plugin-react-swc": "^3.5.0",
"eslint": "^8.56.0",
"eslint-plugin-react": "^7.33.2",
"eslint-plugin-react-hooks": "^4.6.0",
"eslint-plugin-react-refresh": "^0.4.5",
"vite": "^5.1.0"
}
Example: Create the required files and add the following code.
JavaScript
// CheckoutStepper.jsx
import { useEffect, useRef, useState } from "react";
const CheckoutStepper = ({ stepsConfig = [] }) => {
const [currentStep, setCurrentStep] = useState(1);
const [isComplete, setIsComplete] = useState(false);
const [margins, setMargins] = useState({
marginLeft: 0,
marginRight: 0,
});
const stepRef = useRef([]);
useEffect(() => {
setMargins({
marginLeft: stepRef.current[0].offsetWidth / 2,
marginRight: stepRef.current[stepsConfig.length - 1].offsetWidth / 2,
});
}, [stepRef, stepsConfig.length]);
if (!stepsConfig.length) {
return <></>;
}
const handleNext = () => {
setCurrentStep((prevStep) => {
if (prevStep === stepsConfig.length) {
setIsComplete(true);
return prevStep;
} else {
return prevStep + 1;
}
});
};
const calculateProgressBarWidth = () => {
return ((currentStep - 1) / (stepsConfig.length - 1)) * 100;
};
const ActiveComponent = stepsConfig[currentStep - 1]?.Component;
return (
<>
<div className="stepper">
{stepsConfig.map((step, index) => {
return (
<div
key={step.name}
ref={(el) => (stepRef.current[index] = el)}
className={`step ${currentStep >
index + 1 || isComplete ?
"complete" : ""
} ${currentStep === index + 1 ? "active" : ""} `}
>
<div className="step-number">
{currentStep > index + 1 || isComplete ? (
<span>✓</span>
) : (
index + 1
)}
</div>
<div className="step-name">{step.name}</div>
</div>
);
})}
<div
className="progress-bar"
style={{
width: `calc(100% - ${margins.marginLeft
+ margins.marginRight}px)`,
marginLeft: margins.marginLeft,
marginRight: margins.marginRight,
}}
>
<div
className="progress"
style={{ width: `${calculateProgressBarWidth()}%` }}
></div>
</div>
</div>
<ActiveComponent />
{!isComplete && (
<button className="btn" onClick={handleNext}>
{currentStep === stepsConfig.length ? "Finish" : "Next"}
</button>
)}
</>
);
};
export default CheckoutStepper;
JavaScript
// App.jsx
import "./App.css";
import CheckoutStepper from "./components/CheckoutStepper";
const CHECKOUT_STEPS = [
{
name: "Customer Info",
Component: () => <div>Provide your contact details.</div>,
},
{
name: "Shipping Info",
Component: () => <div>Enter your shipping address.</div>,
},
{
name: "Payment",
Component: () => <div>Complete payment for your order.</div>,
},
{
name: "Delivered",
Component: () => <div> Your order has been delivered.</div>,
},
];
function App() {
return (
<div>
<h2>Checkout</h2>
<CheckoutStepper stepsConfig={CHECKOUT_STEPS} />
</div>
);
}
export default App;
JavaScript
// main.jsx
import React from "react";
import ReactDOM from "react-dom/client";
import App from "./App.jsx";
ReactDOM.createRoot(document.getElementById("root")).render(
<React.StrictMode>
<App />
</React.StrictMode>
);
CSS
/* App.css */
body {
font-family: sans-serif;
}
.stepper {
position: relative;
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 20px;
}
.step {
display: flex;
flex-direction: column;
align-items: center;
}
.step-number {
width: 30px;
height: 30px;
border-radius: 50%;
background-color: #ccc;
display: flex;
justify-content: center;
align-items: center;
margin-bottom: 5px;
z-index: 2;
}
.step-name {
font-size: 14px;
}
.active .step-number {
background-color: #007bff;
color: #fff;
}
.complete .step-number {
background-color: #28a745;
color: #fff;
}
.progress-bar {
position: absolute;
top: 25%;
left: 0;
height: 4px;
background-color: #ccc;
}
.progress {
height: 100%;
background-color: #28a745;
transition: 0.2s ease;
}