Go create a login and create a new form endpoint in FormBackend. Give it a name you can remember for example: “Gatsby Contact Form” or something similar for this tutorial. It can always be changed later and is only used for you to remember your form.
Let’s start out by creating a new Gatsby site. If you have an existing Gatsby site you can skip to the next section. Open up a terminal and type the following to start creating your Gatsby site:
npm install -g gatsby-cli
After gatsby-cli is installed, let’s go ahead and create your site
gatsby new formbackend-gatsby
You can of course call the site whatever you want, formbackend-gatsby
is just for the purpose of this tutorial.
Go ahead and start the Gatsby server by cd
to the Gatsby directory you just created.
When in that directory, type the following:
gatsby develop
You can now navigate to the site in your browser by visiting https://localhost:8000
Visit FormBackend and create a new form. Give it a name you can remember for example: “Gatsby Form” or whatever you feel like. It can be anything and is only used so you can find your form later.
Let’s create a new contact us page. We do that by creating a new file src/pages/contact.js
.
First let’s put some basic boilerplate in it:
import * as React from "react" import Layout from "../components/layout" import Seo from "../components/seo" import * as styles from "../components/index.module.css" function ContactPage() { return ( <Layout> <Seo title="Contact us" /> <div className={styles.textCenter}> <h1> Contact us </h1> <form method="POST" action="https://www.formbackend.com/f/664decaabbf1c319"> <div> <label>Name</label> <input type="text" name="name" /> </div> <div> <label>Email</label> <input type="text" name="email" /> </div> <div> <label>Message</label> <textarea name="message"></textarea> </div> <button type="submit">Send message</button> </form> </div> </Layout> ) } export default ContactPage
This gives you an unstyled form with a Name, Email and Message field. Make sure you replace
{your-unique-formbackend-url}
with the unique URL you get from FormBackend when you created a form.
That URL can be found by going to the “Setup” tab for the newly created form.
The form works as is, we can submit it and the submissions go to FormBackend. But we can spice it up a little more and utilize the fact that we have a JacaScript framework.
Let’s make use of JavaScript and submit the form without a page refresh.
First let’s set up some local state, which will keep track of what is entered into the Gatsby form fields.
Towards the top, right after function ContactPage() {
add the following:
const [formData, setFormData] = useState({ name: "", email: "", message: "" });
We need to make sure that name
, email
and message
in the above matches the name
-attributes
of our three form-fields (the two inputs and the textarea).
We need to add an onChange
event handler on each of our inputs, so the text that is entered gets captured in the state we just added.
Let’s look at the name
field as an example:
<input type="text" name="name" onChange="handleInput" />
You of course need to add that to the email
and message
fields as well.
Now let’s go ahead and write the handleInput
function:
const handleInput = (e) => { const fieldName = e.target.name; const fieldValue = e.target.value; setFormData((prevState) => ({ ...prevState, [fieldName]: fieldValue })); }
Now every time you type something in to one of the three form fields, the state object will be updated with the value for the corresponding field.
This happens because we take e.target
which is the field the value is being entered into. Then we find the name
of that field as well as the value
and call
setFormData
where we target the fieldName
key and set the value
. So if you type in the field with name name
- that specific key/value pair will be updated in state.
Time to look at how to submit the entered data, which is now stored in state to FormBackend.
For that we’re going to add a onSubmit
callback on the form:
<form method="POST" action="[your-unique-url]" onSubmit={submitForm}>
Let’s write the submitForm
function:
const submitForm = (e) => { // We don't want the page to refresh e.preventDefault() const formURL = e.target.action const data = new FormData() // Turn our formData state into data we can use with a form submission Object.entries(formData).forEach(([key, value]) => { data.append(key, value); }) // POST the data to the URL of the form fetch(formURL, { method: "POST", body: data, headers: { 'accept': 'application/json', }, }).then(() => { setFormData({ name: "", email: "", message: "" }) }) }
If everything is hooked up according to the above, if you open the network inspector in your browser
you’ll see the data is being submitted to FormBackend. But the values are still in the form after submission? Why is that?
We need to bind each field to the value in the state object. We do that by adding a value
attribute for each field like so:
<div> <label>Name</label> <input type="text" name="name" value={formData.name} /> </div> <div> <label>Email</label> <input type="text" name="email" value={formData.email} /> </div> <div> <label>Message</label> <textarea name="message" value={formData.message}></textarea> </div>
But what is actually happening in the submitForm
function we just wrote? Let’s take a closer look.
When you submit a regular HTML form, the page refreshes. We don’t want that to happen so that’s why we call e.preventDefault()
where e
is the
submission event. We then get the form
itself via e.target
and get the action
attribute off of that.
We need a way to take the submitted data that is stored in the formData
state and turn that in to a proper FormData
object which the browser uses to submit the data to the server.
So we instantiate a new variable data
and assign FormData()
to that. We then iterate over all the entries in our formData
state object and add the key/value pairs to the newly instantiated FormData()
object.
To actually submit the form, we use fetch
which is supported by all modern browsers. We set the method
to POST
which is what FormBackend expects (and the correct way to submit new data over the wire). We assign our FormData
object data
to the body
of the request and set the accept
header to application/json
as we want FormBackend to return json
and not the full HTML page in the response.
Once the form has been submitting successfully, then()
will get run, inside of that we reset our formData
state object by setting all
the field values to blank.
Instead of just having an empty form when it has been submitted, let’s replace it with a message letting the user know that all went well.
In order to do so we need to add a little bit of extra state. So let’s go ahead and add the following:
const [formSuccess, setFormSuccess] = useState(false)
This newly added state will keep track of if form has been submitted successfully. So inside of the then()
callback in our formSubmit
function let’s add setFormSuccess(true)
right after setFormData(...)
so it’ll look like this:
}).then(() => { setFormData({ name: "", email: "", message: "" }) setFormSuccess(true) })
We will then add a conditional to our HTML by using a conditionoal (ternary) operator:
return ( <Layout> <Seo title="Contact us" /> <div className={styles.textCenter}> <h1> Contact us </h1> {formSuccess ? <div>Form submitted</div> : <form method="POST" action="https://www.formbackend.com/f/664decaabbf1c319" onSubmit={submitForm}> <div> <label for="name">Name</label> <input type="text" name="name" onChange={handleInput} value={formData.name} /> </div> <div> <label for="email">Email</label> <input type="text" name="email" onChange={handleInput} value={formData.email} /> </div> <div> <label for="message">Message</label> <textarea name="message" onChange={handleInput} value={formData.message}></textarea> </div> <button type="submit">Send message</button> </form> } </div> </Layout> )
Notice how, if formSubmitted
is true we render a div
with the text “Form submitted”.
We can make this dynamic and use the submission text returned by FormBackend (this can be changed in the settings for your form). Make the following change around the then()
line:
}).then((response) => response.json()) .then((data) => {
We will parse the server response as JSON
which we can access via the data
attribute. We need to add a new piece of state
which we’ll call formSuccessMessage
. Towards the top
of the file we’ll add:
const [formSuccessMessage, setFormSuccessMessage] = useState("")
We then change our “Form submitted” message to <div>{formSuccessMessage}
and below our setFormData
reset in the then
callback for fetch
we add:
setFormSuccessMessage(data.submission_text) `` The JSON returned by FormBackend when you submit the Gatsby form using JavaScript contains the following: ```javascript { submission_text: "Thank you for your submission", redirect_url: null, errors: [], values: { name: "John Doe", email: "hello@formbackend.com", message: "My message" } }
Where values
is the submitted values from our form and submission_text
is the text we have entered on the “Settings” page for your form (default is: Thank you for your submission)
The final version of our code looks like this:
import React, { useState } from "react" import Layout from "../components/layout" import Seo from "../components/seo" import * as styles from "../components/index.module.css" function ContactPage() { const [formData, setFormData] = useState({ name: "", email: "", message: "" }); const [formSuccess, setFormSuccess] = useState(false) const [formSuccessMessage, setFormSuccessMessage] = useState("") const handleInput = (e) => { const fieldName = e.target.name; const fieldValue = e.target.value; setFormData((prevState) => ({ ...prevState, [fieldName]: fieldValue })); } const submitForm = (e) => { // We don't want the page to refresh e.preventDefault() const formURL = e.target.action const data = new FormData() // Turn our formData state into data we can use with a form submission Object.entries(formData).forEach(([key, value]) => { data.append(key, value); }) // POST the data to the URL of the form fetch(formURL, { method: "POST", body: data, headers: { 'accept': 'application/json', }, }).then((response) => response.json()) .then((data) => { setFormData({ name: "", email: "", message: "" }) setFormSuccess(true) setFormSuccessMessage(data.submission_text) }) } return ( <Layout> <Seo title="Contact us" /> <div className={styles.textCenter}> <h1> Contact us </h1> {formSuccess ? <div>{formSuccessMessage}</div> : <form method="POST" action="https://www.formbackend.com/f/664decaabbf1c319" onSubmit={submitForm}> <div> <label for="name">Name</label> <input type="text" name="name" onChange={handleInput} value={formData.name} /> </div> <div> <label for="email">Email</label> <input type="text" name="email" onChange={handleInput} value={formData.email} /> </div> <div> <label for="message">Message</label> <textarea name="message" onChange={handleInput} value={formData.message}></textarea> </div> <button type="submit">Send message</button> </form> } </div> </Layout> ) } export default ContactPage
And that is how you create a contact form for your Gatsby site and gradually make it more advanced to use JavaScript to handle submissions and show a small message after it has been submitted.