This guide will teach you how to create a beautiful and elegant form using Gatsby. Creating contact forms in Gatsby shouldn’t be a tedious task if your site is built using a static/jamstack approach.
In case you're starting a brand new project, you'll need some initial steps. One of the most straightforward ways is to use the gatsby-cli package. To start:
npm install -g gatsby-cli
npm init gatsby
cd my-gatsby-site && npm run develop
Use your favorite code editor to work with files in ~/my-gatsby-site/src. You will be able to make a contact form there.
Create a new file called ContactForm.js in the src folder. You can use any type of field and any framework for building HTML, CSS, and Javascript. For now, let's stick with the standard "Name", "Email", and "Message" for our simple contact form. We're also going to use TailwindCSS to make it beautiful, but again, you can use your own customized CSS code too.
See how to install Gatsby Tailwind CSS here.
1import React, { useState } from "react";
2
3const FORM_ENDPOINT = "https://herotofu.com/start"; // TODO - update to the correct endpoint
4
5const ContactForm = () => {
6 const [submitted, setSubmitted] = useState(false);
7 const handleSubmit = (e) => {
8 e.preventDefault();
9
10 const inputs = e.target.elements;
11 const data = {};
12
13 for (let i = 0; i < inputs.length; i++) {
14 if (inputs[i].name) {
15 data[inputs[i].name] = inputs[i].value;
16 }
17 }
18
19 fetch(FORM_ENDPOINT, {
20 method: 'POST',
21 headers: {
22 Accept: 'application/json',
23 'Content-Type': 'application/json',
24 },
25 body: JSON.stringify(data),
26 })
27 .then((response) => {
28 if (!response.ok) {
29 throw new Error('Form response was not ok');
30 }
31
32 setSubmitted(true);
33 })
34 .catch((err) => {
35 // Submit the form manually
36 e.target.submit();
37 });
38 };
39
40 if (submitted) {
41 return (
42 <>
43 <div className="text-2xl">Thank you!</div>
44 <div className="text-md">We'll be in touch soon.</div>
45 </>
46 );
47 }
48
49 return (
50 <form
51 action={FORM_ENDPOINT}
52 onSubmit={handleSubmit}
53 method="POST"
54 >
55 <div className="pt-0 mb-3">
56 <input
57 type="text"
58 placeholder="Your name"
59 name="name"
60 className="focus:outline-none focus:ring relative w-full px-3 py-3 text-sm text-gray-600 placeholder-gray-400 bg-white border-0 rounded shadow outline-none"
61 required
62 />
63 </div>
64 <div className="pt-0 mb-3">
65 <input
66 type="email"
67 placeholder="Email"
68 name="email"
69 className="focus:outline-none focus:ring relative w-full px-3 py-3 text-sm text-gray-600 placeholder-gray-400 bg-white border-0 rounded shadow outline-none"
70 required
71 />
72 </div>
73 <div className="pt-0 mb-3">
74 <textarea
75 placeholder="Your message"
76 name="message"
77 className="focus:outline-none focus:ring relative w-full px-3 py-3 text-sm text-gray-600 placeholder-gray-400 bg-white border-0 rounded shadow outline-none"
78 required
79 />
80 </div>
81 <div className="pt-0 mb-3">
82 <button
83 className="active:bg-blue-600 hover:shadow-lg focus:outline-none px-6 py-3 mb-1 mr-1 text-sm font-bold text-white uppercase transition-all duration-150 ease-linear bg-blue-500 rounded shadow outline-none"
84 type="submit"
85 >
86 Send a message
87 </button>
88 </div>
89 </form>
90 );
91};
92
93export default ContactForm;
Open src/pages/index.js in your src folder, add contact form component. If it's an existing project, open the file where the contact form should appear. You need to:
1import * as React from "react";
2import ContactForm from "../ContactForm";
3
4const IndexPage = () => {
5 return (
6 <main className="max-w-md p-6 mx-auto my-16 bg-gray-100">
7 <title>Home Page</title>
8 <h1 className="text-xl">Hello World!</h1>
9 <div className="pt-6">
10 <ContactForm />
11 </div>
12 </main>
13 );
14};
15
16export default IndexPage;
Head over to herotofu.com and create an account. It will handle the tedious and complicated form submission process for you. You'll get 14 free days of the free trial. Later, you can leave it with a free forever plan. For the sole purpose of using the contact form, it's usually more than enough.
HeroTofu registration is straightforward. Fill in the basic fields and then confirm your email address.
Once you have confirmed your email address, go to app.herotofu.com/forms to create your first form. Enter your name and email address where you want to receive your form submissions Slack and Zapier are also options, but you need to pay for them once the trial is over.
You'll get the endpoint URL once you hit Submit, so remember to copy that.
Once again, open the ContactForm.js file and fill in the form endpoint URL. You need to change the FORM_ENDPOINT variable at the top of the file. It should look like this.
1import React, { useState } from "react";
2
3const FORM_ENDPOINT = "https://public.herotofu.com/v1/EXAMPLE_FORM_ID";
4
5// The rest of the code...
Done! Go ahead and test your contact form submission! You don't need to do any backend email work, as HeroTofu will handle everything.
As you can see, the implementation is very basic. You only get those fields on the contact form that are visible on the HTML. It might not work if you want something more customized. You'll need to adjust the handleSubmit handler function to inject extra data dynamically. Good examples include the user ID, the selected plan, and some meta-information about site usage. It should make an AJAX call to the FORM_ENDPOINT instead of the regular form submissions. Here's an example of how it might look in practice.
1import React, { useState } from "react";
2
3const FORM_ENDPOINT = "https://herotofu.com/start"; // TODO - update to the correct endpoint
4
5const ContactForm = () => {
6 const [status, setStatus] = useState();
7 const handleSubmit = (e) => {
8 e.preventDefault();
9
10 // Anything you need to inject dynamically
11 const injectedData = {
12 DYNAMIC_DATA_EXAMPLE: 123,
13 };
14 const inputs = e.target.elements;
15 const data = {};
16
17 for (let i = 0; i < inputs.length; i++) {
18 if (inputs[i].name) {
19 data[inputs[i].name] = inputs[i].value;
20 }
21 }
22
23 Object.assign(data, injectedData);
24
25 fetch(FORM_ENDPOINT, {
26 method: 'POST',
27 headers: {
28 Accept: 'application/json',
29 'Content-Type': 'application/json',
30 },
31 body: JSON.stringify(data),
32 })
33 .then((response) => {
34 // It's likely a spam/bot request, so bypass it to validate via captcha
35 if (response.status === 422) {
36 Object.keys(injectedData).forEach((key) => {
37 const el = document.createElement('input');
38 el.type = 'hidden';
39 el.name = key;
40 el.value = injectedData[key];
41
42 e.target.appendChild(el);
43 });
44
45 e.target.setAttribute('target', '_blank');
46 e.target.submit();
47
48 throw new Error('Please finish the captcha challenge');
49 }
50
51 if (response.status !== 200) {
52 throw new Error(response.statusText);
53 }
54
55 return response.json();
56 })
57 .then(() => setStatus("We'll be in touch soon."))
58 .catch((err) => setStatus(err.toString()));
59 };
60
61 if (status) {
62 return (
63 <>
64 <div className="text-2xl">Thank you!</div>
65 <div className="text-md">{status}</div>
66 </>
67 );
68 }
69
70 return (
71 <form
72 action={FORM_ENDPOINT}
73 onSubmit={handleSubmit}
74 method="POST"
75 >
76
77 // The rest of the code is the same...
You send a POST request to the FORM_ENDPOINT of your contact form. But it doesn't matter which way you do it. It can be a regular form submit with ajax handler upon form submission or a completely different request that doesn't involve HTML forms at all
Treat it as a REST API because as long as you're sending POST, you'll be good to go. Your react form submission will reach your inbox.