Simple form submission with Gatsby on Netlify
For a complete solution, scroll to the bottom of the post.
Introduction
I am running a Gatsby blog deployed on Netlify. One of the problems I faced when developing this blog was the contact form submission. Since my contact form was part of dynamically rendered component, Netlify form submission was not working at all.
Luckily, there is a fix that is also described in the Netlify docs.
The code
React form component
Specify prop data-netlify="true"
on the <form>
element. Add onSubmit
handler and
don't forget to add hidden input element with form name into the form body. It must contain
the name attribute with following value: name="form-name"
.
You can also specify more input fields with specified name attribute.
export const FormComponent = (_props) => {
const handleSubmit = (event) => {/* */}
return (
<form
data-netlify="true"
name="contact"
method="post"
onSubmit={handleSubmit}
>
<input type="hidden" name="form-name" value="contact"/>
...
</form>
)
}
onSubmit
handler
The onSubmit
handler searches for all fields within the input form. These fields
are transformed into a key-value paired object. Each key consists of the field's name
attribute.
Each value is the user's input.
Such object is then encoded with an encode
function, see below.
When the form submission succeeds, the user is redirected to a specified page, which
in my case is the /thank-you/
page. I am using the Gatsby's navigate helper function
to redirect to another page.
const handleSubmit = (event) => {
event.preventDefault()
const formFields = event.target.querySelectorAll("[name]");
const formData = Array.from(formFields)
.filter(field => field.name)
.reduce((formData, input) => ({
[input.name]: input.value,
...formData,
}), {})
fetch("/", {
method: "POST",
headers: {"Content-Type": "application/x-www-form-urlencoded"},
body: encode({
"form-name": event.target.getAttribute("name"),
...formData,
})
})
.then(() => navigate("/thank-you/")) // You can replace this with your URL
.catch(error => alert(error))
}
encode
function
This is the encode
function which can be defined outside the React component:
const encode = (data) => Object.keys(data)
.map(key => encodeURIComponent(key) + "=" + encodeURIComponent(data[key]))
.join("&")
The result
import React from "react";
import {navigate} from "gatsby-link";
const encode = (data) => Object.keys(data)
.map(key => encodeURIComponent(key) + "=" + encodeURIComponent(data[key]))
.join("&")
export const FormComponent = (_props) => {
const handleSubmit = (event) => {
event.preventDefault()
const formFields = event.target.querySelectorAll("[name]");
const formData = Array.from(formFields)
.filter(field => field.name)
.reduce((formData, input) => ({
[input.name]: input.value,
...formData,
}), {})
fetch("/", {
method: "POST",
headers: {"Content-Type": "application/x-www-form-urlencoded"},
body: encode({
"form-name": event.target.getAttribute("name"),
...formData,
})
})
.then(() => navigate("/thank-you/")) // You can replace this with your URL
.catch(error => alert(error))
}
return (
<form
data-netlify="true"
name="contact"
method="post"
onSubmit={handleSubmit}
>
<input type="hidden" name="form-name" value="contact"/>
<div>
<label htmlFor="visitorName">Name</label>
<input type="text" name="visitorName" id="visitorName"/>
</div>
<div>
<label htmlFor="visitorEmail">Email</label>
<input type="email" name="visitorEmail" id="visitorEmail"/>
</div>
<div>
<label htmlFor="contactFormSubject">Subject</label>
<input type="text" name="contactFormSubject" id="contactFormSubject"/>
</div>
<div>
<label htmlFor="contactFormMessage">Message</label>
<textarea name="contactFormMessage" id="contactFormMessage" />
</div>
<div>
<input type="submit" />
</div>
</form>
)
}