Building Serverless React Forms Using React Hook Form & Getform.io

Andy Crouch
6 min readFeb 9, 2021

The speed of development you can achieve using serverless architectures and services can be mind-boggling. For small projects, having the ability to connect several services, each having a specific purpose, is very powerful. Recently, I built an MVP that used GetForm.io and Zapier to connect a GatsbyJS web app to Mailchimp. The whole process took just a couple of hours.

The aim of the project was to build a long contact form which collected a lot of information from the user. As the web project was built in GatsbyJS, I was building a React form. Small forms in React are straightforward and it is trivial to handle validation. When you build larger forms with many fields, each of which has different validation rules, it is easy for your code to run away from you.

React-Hook-Form

After a search to see if there were any packages that would help make it easier to build the form, I found React Hook Form. Its strapline was “Performant, flexible and extensible forms with easy-to-use validation.” It sounded exactly what I needed. I was able to build the form using standard HTML controls. React Hook Form can register and track the form fields using a registration mechanism using the ref attribute of your controls. They provide easy access to validation errors and as a nice touch, you can “watch” your field values in the console while building and debugging your forms.

To get started, add react-hook-form to your project:

$ npm install react-hook-form

A very basic example form component would look similar to this:

import * as React from "react";
import { useForm } from "react-hook-form";
export default () => {
const { handleSubmit, register, watch, errors } = useForm();
const onSubmit = (data) => console.info(data); console.info(watch("firstName"));
console.info(watch("lastName"));
console.info(watch("age"));
return (
<form onSubmit={handleSubmit(onSubmit)}>
<input name="firstName" ref={register({ required: true })} />
{errors.firstName && <span>First Name Is Required</span>}
<input name="lastName" ref={register({ required: true })} />
{errors.lastName && <span>Last Name Is Required</span>}
<input name="age" defaultValue="21" ref={register()} /> <input type="submit" />
</form>
);
};

As you can see in the above example:

  • We import react-hook-form.
  • We define the methods we need using useForm().
  • We define an onSubmit handler to fire when a user submits the form. This will receive a data parameter from the hook and you can still define an e parameter to access the target form if you need to.
  • We then define the form. For the onSubmit form handler, we pass in the handleSubmit hook method, passing in our own onSubmit event handle that we have defined.
  • We then just define our form fields as we would but we register them by applying a ref attribute which is used to register the field to the form. This is done by calling the register method passing in any validation rules that need to be applied. All fields need to have a unique name attribute applied.
  • Validation errors are handled via the errors object and each registered field becomes a property to the object. You can see in the first and last name fields I have made them mandatory and if the form is submitted without them being completed the associated validation message are displayed.

When the onSubmit handler fires all of the data in your form is passed as the data parameter. This means you are free to process it as you need. The watch method I have used above is a useful utility to allow you to track and test your code as you develop it. By passing in the name of the field you want to watch, you can see in real-time the changes as you type them in your form in your browser dev tools.

You should read the documentation in full here to find out what else is possible using React Hook Form.

Getform.io

GetForm.io is a really simple service that provides a flexible way to store form data from your site. I have used it in multiple projects for collecting email addresses or contact form data. It works by allowing you to set up a form which has a unique endpoint. You apply the endpoint using the action attribute on your form and ensuring that the method attribute is set to “POST”. This will then post your form data straight to your form in GetForm.io. You can then export your data or set up integrations to automatically sync it with other applications or databases. It is so simple they have an example on their Home Page.

They also have an API which can be used to fire your data into your form which is what I opted to use in my lengthy contact form. The code below shows how this would work in a scaled-down manner.

import axios from "axios";
import * as React from "react";
import { useForm } from "react-hook-form";
export default () => {
const [serverState, setServerState] = useState({ submitting: false, status: null });
const handleServerResponse = (ok, msg, form) => {
setServerState({ submitting: false, status: { ok, msg } });
if (ok) {
// show success message
// handle success next steps
return;
}
// show failure message
// handle failure next steps
return;
};
const onSubmit = (data, e) => {
const formData = new FormData();
for (var field in data) {
formData.append(field, data[field]);
}
setServerState({ submitting: true }); // replace the url parameter with your own GetForm.io form id/url
axios({
method: "post",
url: "https://getform.io/f/11111111-aaaa-1111-aaaa-111111111111",
data: formData,
})
.then((r) => {
e.target.reset();
handleServerResponse(true, "Form Submitted", e);
})
.catch((r) => {
handleServerResponse(false, r.response.data.error, e);
});
};
const { handleSubmit, register, watch, errors } = useForm(); console.info(watch("firstName"));
console.info(watch("lastName"));
console.info(watch("age"));
return (
<form onSubmit={handleSubmit(onSubmit)}>
<input name="firstName" ref={register({ required: true })} />
{errors.firstName && <span>First Name Is Required</span>}
<input name="lastName" ref={register({ required: true })} />
{errors.lastName && <span>Last Name Is Required</span>}
<input name="age" defaultValue="21" ref={register()} /> <input type="submit" />
</form>
);
};

The changes here are very straightforward:

  • I define a state variable serverState to track when the form is waiting for the server to respond. This is useful to control UI elements when posting your data.
  • I update the onSubmit function to receive the e parameter to be able to access the form. I also create a new FormData object and populate it with the fields passed in the data parameter.
  • I then use an axios promise to post the form data to my GetForm.io form (remember to use the correct url as specified in your form settings) and handle the response accordingly.
  • In the example, I define a handleServerResponse function which takes some params for me to handle the axios response. In my real-world example, I used this function to show a notification to the user for example. The important thing to note is to ensure you update the serverState variable. You can also see I use the e param in the then promise handler to reset the form back to its original state.

Zapping GetForm.io To Mailchimp

Once the form was working and the data was saving to GetForm.io all that was left was to ensure that when a user submitted the contact form that they received a Thank You email. This was already set up on Mailchimp which was being used for automated email workflows. I set up a Zapier Zap to connect my GetForm.io form and the Mailchimp account. This approach is really flexible and allows you to select the fields that you want to sync with your Mailchimp audience.

Wrapping up

I really like React Hook Form as it makes stuff super simple and form building can be a horrible task no matter what your tech stack. I still have ASP related nightmares even after all of this time.

I am really just scratching the surface of what you can do with serverless architecture, there is a so much more available. The power for me in using these services is being able to quickly prototype and get running functionality that previously could have taken a few days previously.

--

--

Andy Crouch

Experienced CTO, technical architect, board advisor & technical leader. I focus on building distributed applications & great teams.