Clue Mediator

How to create step wizard in React

📅September 21, 2020

Today we’ll show you how to create step wizard in React without any plugin. In this article, we will use the pure CSS and ReactJS state to create an example using React Hooks.

Here we will create a multi-step wizard that you can use to collect information through steps like signup form, order tracking form, etc.

Steps to create step wizard in React

  1. Create react application
  2. Add bootstrap in application
  3. Design a page to create a step wizard
  4. Handle Back/Next buttons click event
  5. Output

1. Create react application

Let’s create a react application using the `create-react-app` npm package. Run the following command to create an app.

npx create-react-app step-wizard-react

Check out the following article for more guidance.

Create React Application

2. Add bootstrap in application

In this article, we have to add the bootstrap in react application. For quick installation, we will add the bootstrap link in the `public/index.html` page.

<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css">

3. Design a page to create a step wizard

Now, we will create a step wizard component with pure CSS. Let’s consider the 4 steps to create a multi-step wizard. For the demo example, we will take four different components to render on selection.

const firstComponent = () => {
  return <div>First Component</div>
}
const secondComponent = () => {
  return <div>Second Component</div>
}
const thirdComponent = () => {
  return <div>Third Component</div>
}
const finalComponent = () => {
  return <div>Final Component</div>
}

Let’s consider the following array to create a multi-step wizard.

[
  { key: 'firstStep', label: 'My First Step', isDone: true, component: firstComponent },
  { key: 'secondStep', label: 'My Second Step', isDone: false, component: secondComponent },
  { key: 'thirdStep', label: 'My Third Step', isDone: false, component: thirdComponent },
  { key: 'finalStep', label: 'My Final Step', isDone: false, component: finalComponent },
]

In the above array, we have considered the `isDone` property to indicate the completion of the step.

We will use the above array with the React Hooks to manage the step wizard. Check out the following code.

function App() {

  const [steps, setSteps] = useState([
    { key: 'firstStep', label: 'My First Step', isDone: true, component: firstComponent },
    { key: 'secondStep', label: 'My Second Step', isDone: false, component: secondComponent },
    { key: 'thirdStep', label: 'My Third Step', isDone: false, component: thirdComponent },
    { key: 'finalStep', label: 'My Final Step', isDone: false, component: finalComponent },
  ]);

  const [activeStep, setActiveStep] = useState(steps[0]);

  const handleNext = () => {

  }

  const handleBack = () => {

  }

  return (
    <div class="App">
      <h4>Step wizard in React - <a href="https://www.cluemediator.com" title="Clue Mediator" target="_blank" rel="nofollow noopener noreferrer">Clue Mediator</a></h4>
      <div class="box">
        <div class="steps">
          <ul class="nav">
            {steps.map((step, i) => {
              return <li key={i} class="{`${activeStep.key" =="=" step.key="" ?="" 'active'="" :="" ''}="" ${step.isdone="" 'done'="" ''}`}="">
                <div>Step {i + 1}<br><span>{step.label}</span></div>
              </li>
            })}
          </ul>
        </div>
        <div class="step-component">
          {activeStep.component()}
        </div>
        <div class="btn-component">
          <input type="button" value="Back" onclick={handleBack} disabled =="=" activestep.key}="">
          <input type="button" value="{steps[steps.length" -="" 1].key="" !="=" activestep.key="" ?="" 'next'="" :="" 'submit'}="" onclick={handleNext}>
        </div>
      </div>
    </div>
  );
}

export default App;

Add the following css to design a wizard.

index.css

body {
  margin: 20px;
  font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
    'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
    sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
}
code {
  font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New',
    monospace;
}


.box {
  width: 800px;
  border: 1px solid #ddd;
}
.box .steps {
  border-bottom: 1px solid #ddd;
}
.box .steps ul {
  overflow: hidden;
}
.box .steps ul li div {
  color: #999;
  padding: 10px 0 15px 45px;
  position: relative;
  background: #f5f5f5;
  width: 165px;
}
.box .steps ul li div span {
  font-size: 13px;
}
.box .steps ul li:first-child div {
  width: 135px;
  padding-left: 15px;
}
.box .steps ul li div::before {
  content: " ";
  border-top: 50px solid transparent;
  border-bottom: 50px solid transparent;
  border-left: 30px solid #ddd;
  position: absolute;
  top: 50%;
  margin-top: -50px;
  left: 100%;
  z-index: 1;
  margin-left: 1px;
}
.box .steps ul li div::after {
  content: " ";
  border-top: 50px solid transparent;
  border-bottom: 50px solid transparent;
  border-left: 30px solid #f5f5f5;
  position: absolute;
  top: 50%;
  margin-top: -50px;
  left: 100%;
  z-index: 2;
}
.box .steps ul li.done div {
  border-color: #20a8d8 !important;
  color: #fff !important;
  background: #20a8d8 !important;
}
.box .steps ul li.done div::after {
  border-left: 30px solid #20a8d8;
}
.box .steps ul li.active div {
  border-color: #167495 !important;
  color: #fff !important;
  background: #167495 !important;
}
.box .steps ul li.active div::after {
  border-left: 30px solid #167495;
}

.box .step-component {
  padding: 20px;
  height: 200px;
  display: flex;
  align-items: center;
  justify-content: center;
}
.box .btn-component {
  padding: 20px;
  display: flex;
  justify-content: space-between;
}

4. Handle Back/Next buttons click event

After implementing the wizard, let’s write a logic to handle button click events. Check out the following code to manage the Next/Back button clicks.

const handleNext = () => {
  if (steps[steps.length - 1].key === activeStep.key) {
    alert('You have completed all steps.');
    return;
  }

  const index = steps.findIndex(x => x.key === activeStep.key);
  setSteps(prevStep => prevStep.map(x => {
    if (x.key === activeStep.key) x.isDone = true;
    return x;
  }))
  setActiveStep(steps[index + 1]);
}

const handleBack = () => {
  const index = steps.findIndex(x => x.key === activeStep.key);
  if (index === 0) return;

  setSteps(prevStep => prevStep.map(x => {
    if (x.key === activeStep.key) x.isDone = false;
    return x;
  }))
  setActiveStep(steps[index - 1]);
}

5. Output

Let’s combine all code together to see how it looks.

App.js

import React, { useState } from 'react';

const firstComponent = () => {
  return <div>First Component</div>
}
const secondComponent = () => {
  return <div>Second Component</div>
}
const thirdComponent = () => {
  return <div>Third Component</div>
}
const finalComponent = () => {
  return <div>Final Component</div>
}

function App() {

  const [steps, setSteps] = useState([
    { key: 'firstStep', label: 'My First Step', isDone: true, component: firstComponent },
    { key: 'secondStep', label: 'My Second Step', isDone: false, component: secondComponent },
    { key: 'thirdStep', label: 'My Third Step', isDone: false, component: thirdComponent },
    { key: 'finalStep', label: 'My Final Step', isDone: false, component: finalComponent },
  ]);

  const [activeStep, setActiveStep] = useState(steps[0]);

  const handleNext = () => {
    if (steps[steps.length - 1].key === activeStep.key) {
      alert('You have completed all steps.');
      return;
    }

    const index = steps.findIndex(x => x.key === activeStep.key);
    setSteps(prevStep => prevStep.map(x => {
      if (x.key === activeStep.key) x.isDone = true;
      return x;
    }))
    setActiveStep(steps[index + 1]);
  }

  const handleBack = () => {
    const index = steps.findIndex(x => x.key === activeStep.key);
    if (index === 0) return;

    setSteps(prevStep => prevStep.map(x => {
      if (x.key === activeStep.key) x.isDone = false;
      return x;
    }))
    setActiveStep(steps[index - 1]);
  }

  return (
    <div class="App">
      <h4>Step wizard in React - <a href="https://www.cluemediator.com" title="Clue Mediator" target="_blank" rel="nofollow noopener noreferrer">Clue Mediator</a></h4>
      <div class="box">
        <div class="steps">
          <ul class="nav">
            {steps.map((step, i) => {
              return <li key={i} class="{`${activeStep.key" =="=" step.key="" ?="" 'active'="" :="" ''}="" ${step.isdone="" 'done'="" ''}`}="">
                <div>Step {i + 1}<br><span>{step.label}</span></div>
              </li>
            })}
          </ul>
        </div>
        <div class="step-component">
          {activeStep.component()}
        </div>
        <div class="btn-component">
          <input type="button" value="Back" onclick={handleBack} disabled =="=" activestep.key}="">
          <input type="button" value="{steps[steps.length" -="" 1].key="" !="=" activestep.key="" ?="" 'next'="" :="" 'submit'}="" onclick={handleNext}>
        </div>
      </div>
    </div>
  );
}

export default App;

Run the project and check the output in the browser.

Output - How to create step wizard in React - Clue Mediator

Output - How to create step wizard in React - Clue Mediator

That’s it for today.
Thank you for reading. Happy Coding..!!

Demo & Source Code

Github Repository StackBlitz Project