Multi-step forms with Transition Effects in React
Tags: javascript, webdev, react
Published: 2022-07-14 21:26:06

by [Mandah Israel](https://blog.openreplay.com/authors/mandah-israel)

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:

![1](https://blog.openreplay.com/a8e9f27d402704e28ea168ba5ed06536/image01.gif)

## 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.

```javascript
const SignUp = () => {
  return (
    <div className="card">
      <div className="step-title">Sign Up</div> 
      <input
        type="text"
        placeholder="Full Name"
        className="form-group"
      />
      <input
        type="text"
        className="form-group"
        placeholder="Username"
      />
      <input
        type="text"
        className="form-group"
        placeholder="Password"
      />
      <button>
        Next
      </button>
    </div>
  );
};

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.

```javascript
const PersonalInfo = () => {
  return (
    <div className="card">
      <div className="step-title">Personal Info</div>
      <input
        type="text"
        placeholder="Nickname"
      />
      <input
        type="text"
        placeholder="Email"
      />
      <button>
        Next
      </button>
      <br/>
      <button>
        Previous
      </button>
    </div>
  );
};

export default PersonalInfo
```

Create a `LocationInfo.jsx` file in the **src/components** folder and paste the code snippet into it.

```javascript
const LocationInfo = () => {
  return (
    <div className="card">
      <div className="step-title">Location Info</div>
      <input
        type="text"
        placeholder="Address"
      />
      <input
        type="text"
        placeholder="Nationality"
      />
      <input
        type="text"
        placeholder="Zipcode"
      />

      <button>
        Next
      </button>
      <br />
      <button>
        Previous
      </button>
    </div>
  );
};
  
export default LocationInfo;
```

Finally, create an `OtherInfo.jsx` file in the **src/components** folder and paste the code snippet into it.

```javascript
const OtherInfo = () => {
  return (
    <div className="card">
      <div className="step-title">Other Info</div>
      <input
        type="text"
        placeholder="Highest Qualification"
      />
      <input
        type="text"
        placeholder="Occupation"
      />
      <textarea
        type="text"
        placeholder="About"
      />
      <br />
      <button> 
        Submit
      </button>
      <br />
      <button>
        Previous
      </button>
    </div>
  );
};

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.

```javascript
function App() {
  return (
    <div className="App">
      <div className="progress-bar"></div>
      <div>Forms</div>
    </div>
  );
}
```

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](https://github.com/openreplay/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._

![replayer.png](https://static.openreplay.com/images/banner-blog.png)

_Start enjoying your debugging experience - [start using OpenReplay for free](https://github.com/openreplay/openreplay)._




## 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.

```javascript
import { useState } from "react";
```

Instantiate the `useState` hook in the `App()` component.

```javascript
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.

```javascript
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.

```javascript
function App() {
  const [page, setPage] = useState(0);

  const componentList = [
    <SignUp />,
    <LocationInfo />,
    <PersonalInfo />,
    <OtherInfo />,
  ];

  ...
}
```

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.

```javascript
return (
    <div className="App">
    <div className="progress-bar"></div>       
    <div>{componentList[page]}</div>
  </div>
);
```

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.

```javascript
const componentList = [
  <SignUp
    page={page}
    setPage={setPage}
  />,
  <PersonalInfo
    page={page}
    setPage={setPage}
  />,
  <LocationInfo
    page={page}
    setPage={setPage}
  />, 
  <OtherInfo
    page={page}
    setPage={setPage}
  />,
];
```

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.

```javascript
const SignUp = ({page, setPage}) => {
  return ( 
      ...

      <button
          onClick={() => {
            setPage(page + 1);
          }}>
          Next
        </button>
    </div>
  );
};
```

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.

```javascript
const PersonalInfo = ({page, setPage}) => {
  return ( 
      ...

      <button
        onClick={() => {
          setPage(page + 1);
        }}>
        Next
      </button>
      <br/>
      <button
        onClick={() => {
          setPage(page - 1);
        }}>
        Previous
      </button>
    </div>
  );
};

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.

```javascript
const LocationInfo = ({page, setPage}) => {
  return ( 
      ...

      <button
        onClick={() => {
          setPage(page + 1);
        }}>
        Next
      </button>
      <br />
      <button
        onClick={() => {
          setPage(page - 1); 
        }}>
        Previous
        </button>
    </div>
  );
};
```

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.

```javascript
const OtherInfo = ({ page, setPage }) => {
  return (    
      ...

      <button
        onClick={() => {
          alert("You've successfully submitted this form");
        }}>
        Submit
      </button>
      <br />
      <button
        onClick={() => {
          setPage(page - 1);
        }}>
        Previous
      </button>
    </div>
  );
};
```

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.

![2](https://blog.openreplay.com/64b6a54aebd7edbd7c0d224d0cf95d71/image02.gif)


## 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.

```javascript
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`.

```javascript
const componentList = [
  <SignUp
    formData={formData}
    setFormData={setFormData}
    page={page}
    setPage={setPage}
  />,
  <PersonalInfo
    formData={formData}
    setFormData={setFormData}
    page={page}
    setPage={setPage}
  />,
  <LocationInfo
    formData={formData}
    setFormData={setFormData}
    page={page}
    setPage={setPage}
  />,
  <OtherInfo
    formData={formData}
    setFormData={setFormData}
    page={page}
    setPage={setPage}
  />,
];
```

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.

```javascript
const SignUp = ({ page, setPage, formData, setFormData }) => {  // grabbing the props
  return (
      <div>
        <input
          type="text"
          placeholder="Full Name"
          value={formData.fullName} //setting the value of the form to the props value
          onChange={(e) =>
            setFormData({ ...formData, fullName: e.target.value })  //setting the formData to the value input of the textfield 
          }
        />
        <input
          type="text"
          placeholder="Username"
          value={formData.username}
          onChange={(e) =>
            setFormData({ ...formData, username: e.target.value }) 
          }
        />
        <input
          type="text"
          placeholder="Password"
          value={formData.password}
          onChange={(e) =>
            setFormData({ ...formData, password: e.target.value })
          }
        />

      ...
      </div>
  );
};
```

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.

```javascript
const PersonalInfo = ({ page, setPage, formData, setFormData }) => {
  return (
      <div>
        <input
          type="text"
          placeholder="Nickname"
          value={formData.nickname}
          onChange={(e) => setFormData({ ...formData, nickname: e.target.value })}
        />
        <input
          type="text"
          placeholder="Email"
          value={formData.email}
          onChange={(e) => setFormData({ ...formData, email: e.target.value })}
        />

      ...
      </div> 
  );
};
```

Head over **src/components/LocationInfo.jsx** file and update the component.

```javascript
const LocationInfo = ({ page, setPage, formData, setFormData }) => {
  return (
      <div>
        <input
          type="text"
          placeholder="Address"
          value={formData.address}
          onChange={(e) => setFormData({ ...formData, address: e.target.value })}
        />
        <input
          type="text"
          placeholder="Nationality"
          value={formData.nationality}
          onChange={(e) => setFormData({ ...formData, nationality: e.target.value })}
        />
        <input
          type="text"
          placeholder="Zipcode"
          value={formData.zipcode}
          onChange={(e) => setFormData({ ...formData, zipcode: e.target.value })}
        />

      ...
      </div>
  );
};
```

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.

```javascript
const OtherInfo = ({ page, setPage, formData, setFormData }) => {
  return (
      <div>
        <input
          type="text"
          placeholder="Occupation" 
          value={formData.occupation}
          onChange={(e) =>
            setFormData({ ...formData, occupation: e.target.value })
          }
        />
        <textarea
          type="text"
          placeholder="About"
          value={formData.about}
          onChange={(e) => setFormData({ ...formData, about: e.target.value })}
        />

      ...
      </div>
  );
};
```

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.

![3](https://blog.openreplay.com/513c860499df77d508576d3ff8b588d5/image03.gif)


## Create 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.

```javascript
return (
  <div className="App">
    <div className="progress-bar">
      <div style={{width: page === 0? "25%": page === 1? "50%": page === 2? "75%" : "100%"}}></div>
    </div> 

  ...
  </div>
);
```

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.

![4](https://blog.openreplay.com/2c94f9942ce656a66b5bf9492cdf3a22/image04.gif)

## 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 (
      <motion.div                            //updated the div tag
          initial={{ x: x }}
          transition={{ duration: 1 }}
          animate={{ x: 0 }}
        >

      ...
      </motion.div>
    );
}
```

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.

```javascript
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`.

```javascript
const componentList = [
  <SignUp 
    formData={formData}
    setFormData={setFormData}
    page={page}
    setPage={setPage}
    x={x}
    setX={setX}
  />,
  <PersonalInfo
    formData={formData}
    setFormData={setFormData}
    page={page}
    setPage={setPage}
    x={x}
    setX={setX}
  />,
  <LocationInfo
    formData={formData}
    setFormData={setFormData}
    page={page}
    setPage={setPage}
    x={x}
    setX={setX}
  />,
  <OtherInfo
    formData={formData}
    setFormData={setFormData}
    page={page}
    setPage={setPage}
    x={x}
    setX={setX}
  />,
];
```

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.

```javascript
<button
  onClick={() => {
    setPage(page - 1);
    setX(-1000); //here, we're manipulating the xstate
  }}
>
  Previous
</button>
```

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`.
     
```javascript
<button onClick={() => {setPage(page + 1); setX(1000);}}>
  Next
</button>
```

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.

![5](https://blog.openreplay.com/a8e9f27d402704e28ea168ba5ed06536/image05.gif)


## 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](https://github.com/mandah-israel/multi-step-form).



[![newsletter](https://blog.openreplay.com/static/e6b18eda7a327511296a2f27692f9c70/e9957/newsletter.png)](https://newsletter.openreplay.com/)