Introduction
We've all seen a stepper before right? No, not the fitness machine that simulates walking up a flight of stairs. A stepper is a piece of UI that shows you where you are in a list of tasks, questions, steps, etc,. They provide a wizard-like workflow and can are sometimes used as navigation. They're also pretty easy to create with React and the useState
hook.
Full disclosure, I didn't come up with the idea of this project on my own. I'm still refreshing my React knowledge with Jonas Schmedtmann's React course. This is a practice project from the course. Last week I wrote a post detailing how I created a developer profile card from the same course. If you're interested learning how I did it, be sure to check it out!
What I've been doing with these projects is pausing the video, and attempting to complete them on my own. I find this helps me find out what I already know while forcing me to challenge myself to figure out the things I don't. This is a recommended approach if you struggle with imposter syndrome like I do. Exploring your own solutions to tutorials is a great way to test and solidify your knowledge. Let's get started.
Prerequisites
In this post, I'm assuming you have a working knowledge of JavaScript and its array methods. I'm also assuming you know some React basics like JSX, how to create components, and how to pass props. In React, props (short for properties) are a way of passing data from parent components to child components. They make our components more versatile and reusable by allowing them to behave differently based on the input they receive. If you need a refresher on any of this, I recommended checking out the following resources:
- MDN JavaScript guide & reference
- freecodecamp.org
- JavaScript30 by Wes Bos
- Codecademy's React course
Getting Started with the Step Progress Component
Let's dive in and create our own Step Progress Component with React and useState.
Setting up the Project
First, we need to set up a new React project. If you don't already have one, you can create a new one using Create React App.
npx create-react-app step-progress-component
I normally use Vite for creating React projects, but since this tutorial called for CRA, I stuck with it.
Once you've created your React project, navigate to the project directory and start your local development server.
cd step-progress-component
npm start
You should now have a new React project running on http://localhost:3000
.
Remove boilerplate code
Create React App gives us a lot of code/files that we may not always need. In this case, we don't need App.css
, App.test.js
, reportWebVitals.js
, setupTests.js
, nor logo.svg
. In index.js
, remove and trace of reportWebVitals
. In App.js
remove the imports for App.css
and logo.svg
. Then remove everything between the first div
, and delete className="App"
.
Jonas provided the CSS for this project, so we'll be replacing the code in index.css
with that. All I added was the .active
styling for buttons, and extra styling to the body to make it pretty.
* {
box-sizing: border-box;
}
body {
font-family: sans-serif;
color: #333;
min-height: 100vh;
display: grid;
place-content: center;
background-image: radial-gradient(circle, #7950f2, #995ef5, #b36df8, #ca7dfb, #df8eff);
}
.steps {
width: 600px;
background-color: #f7f7f7;
border-radius: 7px;
padding: 25px 100px;
margin: 100px auto;
}
.numbers {
display: flex;
justify-content: space-between;
}
.numbers > div {
height: 40px;
aspect-ratio: 1;
background-color: #e7e7e7;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
font-size: 18px;
}
.numbers .active,
button.active {
background-color: #7950f2;
color: #fff;
}
.message {
text-align: center;
font-size: 20px;
margin: 40px 0;
font-weight: bold;
display: flex;
flex-direction: column;
align-items: center;
}
.buttons {
display: flex;
justify-content: space-between;
}
.buttons button {
border: none;
cursor: pointer;
padding: 10px 15px;
border-radius: 100px;
font-size: 14px;
font-weight: bold;
display: flex;
align-items: center;
gap: 10px;
}
.buttons button span {
font-size: 16px;
line-height: 1;
}
h3 {
margin: 0;
text-transform: uppercase;
}
Creating the Step component
Firstly, let's dive into our Step Component. This component accepts two props, styleClass and number.
export default function Step({ styleClass, number }) {
return <div className={styleClass}>{number}</div>;
}
The styleClass
prop allows us to apply conditional styling to our component depending on what step is active, and the number
prop passes a number to the component based on the current step.
This component is quite simple, but it plays a key role in our application by rendering each step in the progress bar.
Crafting the StepText Component
The StepText
component is responsible for displaying the text associated with each step in our progress bar. We pass a single text
prop to this component, which we then render within an h3
tag.
export default function StepText({ text }) {
return <h3>{text}</h3>;
}
This component is a fantastic example of the principle of "composition" in React, allowing us to keep our components small, focused, and reusable.
Button, button, who's got the button?
Next, we'll move onto the Button
component. This component will render our 'Next' and 'Previous' buttons, and will handle user interactions to move between steps.
export default function Button({ children, onClick, styleClass }) {
return (
<button className={styleClass} onClick={onClick}>
<span>{children}</span>
</button>
);
}
The onClick
prop that we pass to our button allows us to handle user clicks and thus manipulate the active step in our progress bar. The children
prop allows us to pass the button text as children of the Button
component.
Bringing it all together
In App.js
, we need to import all three components.
I recommend placing individual components in a
components
folder in thesrc
directory. This makes for better organization in the long run. Since this is a smaller project, we can get away with not doing this.
Adding Interactivity with useState
With our components ready, let's move on to creating an interactive stepper. For this, we'll be using React's useState
hook. Hooks are a relatively new addition to React that allows us to use state and other React features in our functional components. useState
is one of these hooks and it allows us to add state to our functional components. State can be thought of as a way to keep track of information in your application that can change over time and affect your app's behavior.
In our App.js
, we'll start by importing useState
from React and our three components: Step
, StepText
, and Button
.
import { useState } from 'react';
import Step from './Step';
import StepText from './StepText';
import Button from './Button';
Then, let's define the text for each step of our progress bar. We'll store this in an array called stepText
.
const stepText = ['Learn React ⚛️', 'Apply to jobs 💼', 'Get a job 🎉'];
Creating the State for Our Stepper
Next, we're going to create our step
state. We initialize it to 1 since we want to start at the first step. We use setStep
to change the state.
const [step, setStep] = useState(1);
Handling Step Changes
After that, we'll create two functions handleNext
and handlePrevious
. These functions will allow us to go forward or backward in our progress bar when the next and previous buttons are clicked.
const handleNext = () => {
if (step < stepText.length) {
setStep(step + 1);
}
};
const handlePrevious = () => {
if (step > 1) {
setStep(step - 1);
}
};
Rendering Our Components
Finally, we'll render our components. We create a div for each step and conditionally apply the 'active' class to the current step. We also render the text for the current step and the previous and next buttons, again applying the 'active' class conditionally.
return (
<div className="steps">
<div className="numbers">
{stepText.map((_, index) => (
<Step
styleClass={step === index + 1 ? "active" : ""}
key={`Step ${index + 1}`}
number={index + 1}
/>
))}
</div>
<div className="message">
<StepText text={stepText[step - 1]} />
</div>
<div className="buttons">
<Button styleClass={step > 1 ? "active" : ""} onClick={handlePrevious}>
Previous
</Button>
<Button
styleClass={step < stepText.length ? "active" : ""}
onClick={handleNext}
>
Next
</Button>
</div>
</div>
);
And that's it! We've built a functional Step Progress Component using React and the useState
hook. The full code for App.js
looks like this:
import { useState } from "react";
import Step from "./components/Step";
import StepText from "./components/StepText";
import Button from "./components/Button";
const stepText = ["Learn React ⚛️", "Apply to jobs 💼", "Get a job 🎉"];
function App() {
const [step, setStep] = useState(1);
const handleNext = () => {
if (step < stepText.length) {
setStep(step + 1);
}
console.log(step);
};
const handlePrevious = () => {
if (step > 1) {
setStep(step - 1);
}
console.log(step);
};
return (
<div className="steps">
<div className="numbers">
{stepText.map((_, index) => (
<Step
styleClass={step === index + 1 ? "active" : ""}
key={`Step ${index + 1}`}
number={index + 1}
/>
))}
</div>
<div className="message">
<StepText text={stepText[step - 1]} />
</div>
<div className="buttons">
<Button styleClass={step > 1 ? "active" : ""} onClick={handlePrevious}>
Previous
</Button>
<Button
styleClass={step < stepText.length ? "active" : ""}
onClick={handleNext}
>
Next
</Button>
</div>
</div>
);
}
export default App;
Conclusion
Learning React is all about understanding its core concepts, and useState is definitely one of them. By building this Step Progress Component, not only did we gain a better understanding of how useState works, but we also learned how to structure our code using components and how to pass props to make them more reusable. This is a great step forward in becoming a more proficient React developer. Keep practicing and you'll surely get there!
If you want to see the stepper in action, check out my pen!
Top comments (0)