Building a Scalable Form in React, Without the Headaches

Photo by Eugene Chystiakov on Unsplash

This is the 4th edition of the Breathe Life Engineering Blog. This month’s article is written by Janic Beauchemin.

Life insurance products are mainly about inputting information into big forms. One of our goals at Breathe Life is to make it as simple and as enjoyable as possible to navigate through the (what might seem like endless) questions. So, when we started a new project, we had to first think about how we would implement a form, that could include several hundred questions, in React. All without having to deal with all the pain we usually get while building HTML forms.

The first part was all about research. We needed to find out the optimal combination of tools which would allow us to build this project faster, and actually make us enjoy working with forms. We investigated a bunch of tools and libraries, trying to figure out which would make the process easier. First, we identified what we needed:

  • The ability to save the form as often as needed
  • Validate form input based on a complex set of rules
  • Hide / Disable form elements (or entire sections) based on other form answers
  • Have unified form UI components (text box, select options, radio button, etc.)

Our criteria for choosing a library was mainly focused on 3 things: The popularity of the library; how actively maintained the library was (recent updates, number of collaborators, etc.); the quality of the documentation.

Here’s what we ended up with. It’s what we now think of as the holy grail of form building:

Handling the form

To handle the form, we decided to use Formik. Formik takes care of the form submission, shows errors, applies validation, sets default values, resets the form, etc. It also takes care of mapping your data to the right form element by matching the input name with the data key. As an example, let’s say you have a data object like this:

{ firstName: ‘John’, lastName: ‘Doe’ }

You can then have

<input type=’text’ name=’firstName’ />
<input type=’text’ name=’lastName’ />

And Formik will make sure your input values and the data object are in sync. Basically, Formik is the one who knows everything about form fields. It’s the brains of the operation, its job is to orchestrate everything. Here’s a basic implementation example:

Validation

To validate the form answers and show error messages, we investigated 3 libraries: Joi, Yup and AJV. We decided to go with Yup ( https://github.com/jquense/yup ). Our first thought was to go with Joi, but the lack of support on the frontend, plus the easy integration of Yup with Formik made it a better choice. Also, Yup is heavily inspired by Joi, so they’re similar. It was the perfect match for Formik (the Formik team even recommends using it and gives you integration examples!). Here’s an example of what a validation object looks like:

Fields rendering

This was probably the toughest choice we had to make. We hesitated between Ant Design and Material-UI. Both are very good libraries which have ~45k stars on Github and have similar components. Ant Design has a lot of complex components which Material-UI is lacking, and the library has a lot of momentum at the moment. The main reasons that made us choose Material-UI instead were that it provides the best UX for our needs and its theming system makes it flexible enough to apply some basic customization while not focusing too much on the CSS part. And since our UI was going towards a Material Design look, it was the logical choice for us. We also have the additional benefit of having access to formik-material-ui, which will ease the communication between Material-UI components and Formik. Here’s what a TextField component looks like using formik-material-ui:

Error handling

Now that the fields were set and that we had proper field validation, we needed to show the validation errors in a nice way. Since material design already has good guidelines on how to show form errors, we were able to leverage Material-UI functionality to properly show an error only by passing the error property to a field. Fortunately for us, this step was already handled by formik-material-ui. So, in most cases, we didn’t have anything to do. Unfortunately, not all cases are handled by the library. As an example, if you have a radio button group which is required, we will need to handle the error manually, since there’s no way to show errors related to a group of radio buttons by default. Here’s how to do it:

Saving the form

Since the form we are building is pretty big, we needed to make sure the user didn’t lose any progress if they lost connection to the site or if they closed the browser window. We implemented an autosave feature that would save the form every time there’s an update in it. The autosave function is called every time the user clicks on a radio button, updates a textfield value, etc. We also added a debounce, so we don’t spam our own backend service with too many calls. By saving the form this way, we can be sure that the user won’t lose any progress while filling out the form. Here’s what our auto-save feature looks like:

All we had to do next was to add the AutoSave component inside the Formik render method and pass the form values.

Managing many forms

The other big question we had to ask ourselves was how we should go about creating all the components? We had 2 architecture ideas: component-based and config-based.

The component-based architecture meant that we would have specific input components for basically every field (or almost). So, let’s say we needed a field for a user’s first name, last name, and email. We would have 3 different inputs with different names/validation, etc. It has the advantage of being simple and flexible but has the disadvantage of having to create a ton of components. We can group fields that have similar behavior/validation together, but we would still end up with a lot of React components.

The config-based architecture meant that we have a JSON file with all the data needed to build the form. It has the advantage of being able to easily create different variations of the form without touching the React codebase. One downside is that everything that would change a form input behavior needs to live inside the config, like form validation, if the form is disabled, etc. which could make the config file huge. Also, having to deal with validation based on other components meant that we would need to create a complex system of validation inside the config file.

Ultimately, we ended up using a config-based architecture. We think that it will allow us to scale in a more efficient manner, and it will keep the codebase simple since we will only need a few basic field components. It will also allow us to show different forms for different clients without impacting the code base since we only need to load a different json file to build a completely different form. We based our config structure on 4 levels:

Here’s an example to illustrate how it would work. Let’s say we have:

  1. A section named ‘General Info’.
  2. This section would have a subsection named ‘Employee information’.
  3. Inside this subsection, we would have a question named ‘Address’.
  4. This question would have a few fields like ‘street’, ‘apartment’, ‘city’, ‘country’ and ‘postal code’.

Here’s a simplified view of what kind of information we have on every level:

We also created a system that allows us to hide or disable a section, subsection, question, or field based on other field answers. But that’s an entirely different blog post! We’ll cover that another time.

Conclusion

Using Formik with Yup and Material-UI to handle forms is definitely a step forward in our day to day work. It saves us a ton of time, prevents a lot of headaches and allows us to develop new features faster. We’re always searching for ways to improve the way we work and be more efficient in what we do, but we’re pretty confident that we’re on the right track with those libraries. We encourage you to give it a try if you have to deal with big forms in the future.

If you want a more step by step installation of Formik, Yup and Material-UI in react, here’s an excellent article about it.

That’s all for today, thanks for reading! If you have any question or comment, don’t hesitate to reach out to us at hello@breathelife.com.