Build a multi-step form with React Hooks, Formik, Yup and MaterialUI

Vu Nguyen
6 min readApr 7, 2020

--

Introduction

Form is one of the most important parts of any application. Handling and managing forms in React apps are hard and could be a puzzle, especially for multi-step forms. There are many different libraries out there that can make it easier and Formik is one of them.

Form is one of the most important part of any application. Handling and managing forms in React apps are hard, especially multi-step forms. There are many different libraries out there that can make your life easier and Formik is one of the best choice nowadays.

This post is all about explaining how to build a simple multi-step form with Formik and Yup which is a JavaScript schema builder for value parsing and validation.

For Formik also seamlessly integrates with Material UI (MUI) - a React library that implement Google Material Design, providing many components like TextField, Button, CheckBox, Select, Step and several others out of the box, I decided to use MUI for this project to speed up the UI development and give the app a more decent look.

Here is a quick overview of what we are going to do:

Multi-step checkout form

Simple multi-step checkout form

*This tutorial assumes you have basic knowledge of React and Formik

Installations

Create a new React app using create-react-app

npx create-react-app react-multi-step-form

Using yarn / npm to install necessary dependencies for this project:

"dependencies":{
"@date-io/date-fns":"^2.0.1",
"@material-ui/core":"^4.9.0",
"@material-ui/pickers":"^3.2.10",
"date-fns":"^2.9.0",
"formik":"^2.1.3",
"lodash":"^4.17.15",
"moment":"^2.24.0",
"prop-types":"^15.7.2",
"yup":"^0.28.1"
}

Start the app to check if everything is working fine

yarn start

If you see this page, congratulation!

CRA default page

Let's build the app layout

Break the UI into a component hierarchy

Our app will be separated into 2 main components:

  • MaterialLayout: This component is the template including Header and Footer to create a consistent look and behavior for all other components which are wrapped as its children.
  • CheckoutPage: This component is the multi-step form built with Formik and will be wrapped by MaterialLayout.

Now let's focus on creating MaterialLayout component:

In src folder, create components folder -> create Layout folder -> create MaterialLayout.jsx file

This is what your src folder should look like now:

Open MaterialLayout.jsx file and add the following codes:

In Layout folder, create styles.js file to write the styles for our MaterialLayout component, add the codes below :

As you can see, instead of using CSS or LESS/SASS to write the component styles, I'm using @material-ui/styles which is a CSS-in-JS solution. This overcomes lots of limitations, and unlocks many great features (theme nesting, dynamic styles, self-support, etc.).

For more information about this styling solution, please visit Material UI Styles Official Documentation

Go to src folder -> components folder -> create Header folder -> Create Header.jsx file and add the following codes:

In the same folder, create styles.js file to write the styles and index.js file to export Header component.

styles.js

index.js:

import Header from './Header';
export default Header;

Go to src folder -> components folder -> create Footer folder -> Create Footer.jsx file and add the following codes:

In the same folder, create index.js file to export Footer component.

import Footer from './Footer';
export default Footer;

Alright! We've just finished our template, next up, we need to edit App.js.

Open App.js file, replace the default content with the following codes:

Let's see how our app looks:

Building the form with Formik

In this part, we're gonna build a checkout page including 4 main components:

  • AddressForm.jsx - Step 1
  • PaymentForm.jsx- Step 2
  • ReviewOrder.jsx - Step 3

And some other files for defining form model, form initial values and validation schema:

  • checkoutFormModel.js
  • formInitialValues.js
  • validationSchema.js

Go to src folder -> components folder -> create CheckoutPage folder -> Create CheckoutPage.jsx file and add the following codes:

  • Let’s go through the code we’ve just added.
  • The component is initialized with the default value for step in state as 0, and the first section of our form is rendered. Users can then skip back and forth between steps using setActiveStep function. That function updates the value of step in state so as to allow users to switch between the rendered components.
  • currentValidationSchemea variable is used to validate the form in current step with Yup
  • _sleep function returns a promise to simulate an asynchronous API call
  • _handleSubmit function is our submission handler
  • _submitForm function calls mock API function and alert the form values
  • _handleBack function let users go back to the previous step

Before creating form sections, we should take a closer look at our Yup Validation Schema to know how we can define the validation rules for each section of the form.

Since our form has multiple steps and each step has its own validation rules, we need to create an array with 2 items of Yup ObjectSchema. The order of the items in the schema array need to correspond with the order of the steps in our form so that in the CheckoutPage.jsx, we can get the validation rules appropriate to the current active step this way:

const currentValidationSchema = validationSchema[activeStep];

Thanks to Formik for already providing us a prop called validationSchema which will automatically transform Yup’s validation errors into a pretty object whose keys match values and touched. All we need to do now is pass currentValidationSchemato the Formik component:

<Formik validationSchema="currentValidationSchema">...</Formik>

Next, we will get the first section of our form ready.

Inside AddressForm.jsx place the following code.

This creates a form that collects user's shipping address information such as: First name, Last name, address, city, state,...

You may notice that each form field is provided a name and label which it picks from the formField prop passed from CheckoutPage component

For the section section of our form.

Inside PaymentForm.jsx place the following code.

This section collects the user’s payment information. Everything is just similar to the first section.

Coming down to the final section of our form where the user can review all the details that they've filled in from the beginning. Add the following code to ReviewOrder.jsx

To get all the form values from the first step, we need to find a way to hook into FormikContext to access it's data. And we can use useFormikContext() which is a Formik's custom React hook that will return all Formik state and helpers via React Context.

const { values: formValues } = useFormikContext();

Then we can easily pass the form values to ShippingDetails and PaymentDetails component to show data

ShippingDetails.jsx

PaymentDetails.jsx

That's it! We have now successfully built a multi-step form with validation features in React without pain and suffering using the combination of powerful libraries like Formik, Yup and MaterialUI.

Demo Of Checkout Multi-step Form on CodeSandbox

You can find whole code on my GitHub and CodeSandBox:

Github: https://github.com/nphivu414/react-multi-step-form

CodeSandbox: https://codesandbox.io/s/building-multi-step-form-with-formik-yup-3ro0d

--

--