How to create step wizard in React
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
- Create react application
- Add bootstrap in application
- Design a page to create a step wizard
- Handle Back/Next buttons click event
- 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.
1 | npx create-react-app step-wizard-react |
Check out the following article for more guidance.
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.
1 | <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.
1 2 3 4 5 6 7 8 9 10 11 12 | 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.
1 2 3 4 5 6 | [ { 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.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 | 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 className="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 className="box"> <div className="steps"> <ul className="nav"> {steps.map((step, i) => { return <li key={i} className={`${activeStep.key === step.key ? 'active' : ''} ${step.isDone ? 'done' : ''}`}> <div>Step {i + 1}<br /><span>{step.label}</span></div> </li> })} </ul> </div> <div className="step-component"> {activeStep.component()} </div> <div className="btn-component"> <input type="button" value="Back" onClick={handleBack} disabled={steps[0].key === 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
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 | 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.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | 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
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 | 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 className="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 className="box"> <div className="steps"> <ul className="nav"> {steps.map((step, i) => { return <li key={i} className={`${activeStep.key === step.key ? 'active' : ''} ${step.isDone ? 'done' : ''}`}> <div>Step {i + 1}<br /><span>{step.label}</span></div> </li> })} </ul> </div> <div className="step-component"> {activeStep.component()} </div> <div className="btn-component"> <input type="button" value="Back" onClick={handleBack} disabled={steps[0].key === 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.
That’s it for today.
Thank you for reading. Happy Coding..!!