A multi-step form is a long form that is broken into multiple steps to make it look less intimidating to site visitors, allowing them to complete their information in chunks. It helps to provide a user-friendly and well-organized layout. In this article, we will learn how to create a multi-step form with a transition animation. To follow up with this tutorial, you should be familiar with React and React hooks.
File Architecture
Before diving into the codes, let’s have a look at how our folders and files will be structured.
src
┣ components
┃ ┣ 0therInfo.jsx
┃ ┣ LocationInfo.jsx
┃ ┣ PersonalInfo.jsx
┃ ┗ SignUp.jsx
┣ App.css
┣ App.js
┣ index.css
┗ index.js
We have a components
folder that contains the OtherInfo.jsx
, LocationInfo.jsx
, PersonalInfo.jsx
and SignUp.jsx
. These files contain inputs for different form sections in our app.
Creating Multi-Step Form App
Let’s start by setting up a new React app using the npx
or yarn
command below.
npx create-react-app multi-step-form-app
npm start
// OR
yarn create react-app multi-step-form-app
yarn start
Here is a GIF showing what we will be building in the following sections:
Create the Form components
We’ll be splitting our codes into smaller form components for each form to be displayed. Let’s start by creating a components
folder in the src directory. Afterward, create a SignUp.jsx
file in the src/component folder and paste the code snippet into it.
const SignUp = () => {
return (
Next
);
};
export default SignUp;
In the above code, we create a form using the input
tag and setting the placeholder
to the input description. We'll be doing the same for the next three (3) Form components( PersonalInfo.jsx
, LocationInfo.jsx
, OtherInfo.jsx
). Create a PersonalInfo.jsx
file in the src/components folder and paste the code snippet into it.
const PersonalInfo = () => {
return (
Next
Previous
);
};
export default PersonalInfo
Create a LocationInfo.jsx
file in the src/components folder and paste the code snippet into it.
const LocationInfo = () => {
return (
Next
Previous
);
};
export default LocationInfo; Finally, create an
OtherInfo.jsx
file in the src/components folder and paste the code snippet into it.
const OtherInfo = () => {return (
Submit
Previous
);
}; export default OtherInfo; With this, we have created the layout of the forms we will use to create the functionality of the multi-step form. To finish, replace the entire code in the
App.js
file with the code block below.
function App() {return (
);
} The code block above is the template for our app. We have the
progress-bar
, and the div
that will contain our forms later on.
### Styling our Forms
Let’s add some nice colors and displays to our form. Head over to the App.css
file and replace everything with the code below.
.App {overflow: hidden;
display: flex;
justify-content: center;
flex-direction: column;
align-items: center;
position: relative;
height: 100vh;
background: rgba(194, 233, 251, 1);
background: radial-gradient(
circle,
rgba(161, 196, 253, 1) 10%,
rgba(194, 233, 251, 1) 100%
);
}
.progress-bar {
width: 400px;
height: 15px;
background-color: white;
margin-bottom: 50px;
}
.progress-bar div {
width: 25%;
height: 100%;
background-color: rgb(98, 0, 255);
}
.card {
width: 400px;
background-color: white;
border: 1px solid #333;
border-radius: 0.5rem;
padding: 0.5rem;
max-width: 500px;
margin: 0 auto;
display: flex;
flex-direction: column;
}
.form-group {
display: flex;
flex-direction: column;
margin-bottom: 0.5rem;
gap: 0.25em;
}
.form-group:last-child {
margin: 0;
}
.form-group > label {
font-weight: bold;
font-size: 0.8em;
color: #333;
}
.form-group > input {
border: 1px solid #333;
border-radius: 0.25em;
font-size: 1rem;
padding: 0.25em;
}
.step-title {
margin: 0;
margin-bottom: 1rem;
font-size: 2rem;
text-align: center;
}
input {
border: 2px solid rgb(98, 0, 255);
border-radius: 5px;
height: 35px;
display: block;
margin-bottom: 1rem;
}
.button-area {
display: flex;
align-content: space-between;
}
button {
background-color: #4caf50;
border: none;
color: white;
padding: 0.8rem;
text-align: center;
text-decoration: none;
display: inline-block;
font-size: 16px;
}
textarea {
height: 5rem;
} The styles above will add a nice aesthetic feel to our form, making it more appealing to our users. ### Open Source Session Replay OpenReplay is an open-source, session replay suite that lets you see what users do on your web app, helping you troubleshoot issues faster. OpenReplay is self-hosted for full control over your data. Start enjoying your debugging experience — start using OpenReplay for free. ### Creating dynamic pages In this section, we’ll display our forms dynamically upon clicking the
Next
and Previous
buttons. By doing this, we will be manipulating the a state property. Import the useState
hook from the react library into the App.js
file.
import { useState } from "react";
Instantiate the useState
hook in the App()
component.
function App() {const [page, setPage] = useState(0); ...
} In the code block above, we created a
page
hook and the setPage
for updating the page
hook while setting the initial state to zero (first form) in the useState()
function. Import the components created earlier into the App.js
file.
import SignUp from "./components/SignUp";import PersonalInfo from "./components/PersonalInfo";
import LocationInfo from "./components/LocationInfo";
import OtherInfo from "./components/OtherInfo"; Then, paste the code below into the
App()
component.
function App() {const [page, setPage] = useState(0); const componentList = [
,
,
,
,
]; ...
} In the above code, we created two arrays, the
componentList
array that contains the list of all our forms in the order we want them to display. Next, let's update our User Interface with our dynamic form. Update the return()
in the App.js
with the code below.
return (); In the above code, we display our
componentList
using the page
value as our index.
### Switching Between Pages
In the previous section, we’ve made the pages dynamic, but so far, just a single form page still displays. Let’s change our form pages by clicking the Next and Previous buttons. To achieve this, update the componentList
array.
const componentList = [,
,
,
,
]; In the code block above, we’re passing the
page
and the setPage
into all components as props, enabling us to control the state of each component. Next, Let's accept the props and manipulate the state in each component. Head over to the src/components/SignUp.jsx file and update the code.
const SignUp = ({page, setPage}) => {return (
... {
setPage(page + 1);
}}>
Next
);
}; In the code block above, we’re retrieving the
page
and setPage
props, incrementing by 1
the page
prop upon clicking the Next
button. We'll be doing the same to all the other components. Head over to src/components/PersonalInfo.jsx file and update the code.
const PersonalInfo = ({page, setPage}) => {return (
... {
setPage(page + 1);
}}>
Next
{
setPage(page - 1);
}}>
Previous
);
}; export default PersonalInfo In the code block above, we’re updating the state by incrementing by
1
in the Next
and decrementing it by 1
in the Previous
button. Head over to the src/components/LocationInfo.jsx file and update the code.
const LocationInfo = ({page, setPage}) => {return (
... {
setPage(page + 1);
}}>
Next
{
setPage(page - 1);
}}>
Previous
);
}; Like we did in the previous code block, we’re updating the state by incrementing by
1
in the Next
and decrementing it by 1
in the Previous
button. Finally, head over to the src/components/OtherInfo.jsx file and update the code.
const OtherInfo = ({ page, setPage }) => {return (
... {
alert("You've successfully submitted this form");
}}>
Submit
{
setPage(page - 1);
}}>
Previous
);
}; In the code block above, we’re displaying an alert dialog signifying the end of the form pages. We’re also decreasing the state by
1
to return to the previous form in the Previous
button. Upon checking our process, you should have the results below.
### Preserving entered data across steps
In this section, we’ll be saving our values in our text field when switching pages to allow users to modify their previous data. Head to the App.js
and paste the code below into the App()
component.
function App() {const [formData, setFormData] = useState({
fullname: "",
username: "",
password: "",
nickname: "",
email: "",
address: "",
nationality: "",
zipcode: "",
highestQualification: "",
occupation: "",
about: "",
}); ...
} In the above code, we created a
formData
hook and set its initial value to the object we want to update from our text field. Let's update each component in the componentList
.
const componentList = [,
,
,
,
]; In the above code, we are passing the
formData
and the setFormData
as props into each component in the componentList
. Let's grab the formData
value and update the setFormData
with our form input. Head over src/components/SignUp.jsx file and update the component.
const SignUp = ({ page, setPage, formData, setFormData }) => { // grabbing the propsreturn (
setFormData({ ...formData, fullName: e.target.value }) //setting the formData to the value input of the textfield
}
/>
setFormData({ ...formData, username: e.target.value })
}
/>
setFormData({ ...formData, password: e.target.value })
}
/> ...
);
}; In the above code, we’re grabbing the
formData
and the setFormData
function as props, updating the text field's value to the formData
value and updating the object in the formData
to our typed value. We'll do the same for all the other components. Head over src/components/PersonalInfo.jsx file and update the component.
const PersonalInfo = ({ page, setPage, formData, setFormData }) => {return (
setFormData({ ...formData, nickname: e.target.value })}
/>
setFormData({ ...formData, email: e.target.value })}
/> ...
);
}; Head over src/components/LocationInfo.jsx file and update the component. const LocationInfo = ({ page, setPage, formData, setFormData }) => {
return (
setFormData({ ...formData, address: e.target.value })}
/>
setFormData({ ...formData, nationality: e.target.value })}
/>
setFormData({ ...formData, zipcode: e.target.value })}
/> ...
);
}; In the code above, we’re updating the value and the onchanged of the address, nationality, and zipcode. Finally, Head over src/components/OtherInfo.jsx file and update the component. const OtherInfo = ({ page, setPage, formData, setFormData }) => {
return (
setFormData({ ...formData, occupation: e.target.value })
}
/>
);
}; Here, we’re also updating the value and setting the
formData
to our typed input. With these done, we'll have a form looking like this.
Create a progress display header
### Creating a progress display header
Using a progress bar, let’s show users the current page they’re on. Head to the App.js
file and update the progress-bar
div.
return (); In the code block above, we’re styling the progress bar by increasing the indicator’s width in the
div
tag according to the page
index. Thus, the width will change whenever there is an update to the page
state.
### Adding a transition effect to the form
Let’s give our form some swift animation when navigating to and fro. This section will cover how to add nice animations when navigating to and from the different forms. For this, we will be installing the Framer motion package. Run the command below to install it
npm install framer-motion// OR
yarn add framer-motion We’ll be adding a transition effect to all our form components, so make sure to import it into the various form components (SignUp, PersonalInfo, LocationInfo, and OtherInfo). “javascript import { motion } from “framer-motion”; Now add the `x` prop and update the `div` tag to all the components listed above. ```javascript
const SignUp = ({ formData, setFormData, x}) => { //added x
return (
...
);
} In the above code, we have added an
x
parameter to our prop and updated our div
tag to motion.div
. Then we set our animation to the values from our prop. Note: Ensure to make the above update to the PersonalInfo, LocationInfo, and OtherInfo Component.
Finally, head to the App.js
file and add the hook below.
const [x, setX] = useState(0);
The hook above is added to the App()
function. We'll be using this to control our transition flow. Next, pass the hook into every component in the componentList
.
const componentList = [,
,
,
,
]; Then, control the animation flow on navigation between pages. In the
Previous
buttons, add the update to the x
state. Head over to the PersonalInfo
, LocationInfo
, and OtherInfo
and update the Previous button to the code below.
{setPage(page - 1);
setX(-1000); //here, we're manipulating the xstate
}}
>
Previous
In the code block above, we’re setting the value of our x
state to -1000, making the animation start from left to right. Update the code in the Next
button by updating the x
state in the SignUp
, PersonalInfo
, and LocationInfo
.
Next
In the above code, we’re updating the x
state to 1000 to give the form a right-to-left transition. With all this done, we should be able to achieve a dynamic form with cool transitions.
Conclusion
In this tutorial, we’ve been able to create dynamic forms, preserve form states, form progress bar and add transitions to our form.
The entire source code can be found on GitHub.
Originally published at blog.openreplay.com on July 14, 2022.