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>
    )
}

© 2020 Bno.sk