Go create a login and create a new form endpoint in FormBackend. Give it a name you can remember for example: “NextJS Contact Form” or something similar for this tutorial. It can always be changed later and is only used for you to remember your form.
If you don’t have an existing Next.js app, let’s create one real quick - if you do, then you can skip this section and go to the next one.
Per NextJS own documentation I would check that you have Node.js version 10.13 or later. You can use Node.js own instructions to install it.
Let’s go ahead and create the Next.js app. Go to the directory where you want the
app to be created in your terminal. We’re going to use create-next-app
via npx
to create the app.
npx create-next-app formbackend-nextjs
Let’s cd
into the directory that was just created which contains our Next.js app.
cd formbackend-nextjs
We can start the development server with yarn run dev
. That will start the
Next.js development server, which will automatically reload everytime you make any changes.
We can access our local Next.js app on http://localhost:3000
. Visiting that URL
should show you the Next.js starter page and a big “Welcome to Next.js!” headline.
Go ahead and create a new file named contact.js
in the pages
directory.
We’ll give it the following content for now:
import React, { useState } from "react" export default function Contact() { return ( <div> <h1>Contact form</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> ) }
This is a simple form with three form fields: name
, email
and message
. You can
access the page if you visit http://localhost:3000/contact
. Before doing anything
change the action
attribute of the form tag, to the URL that matches the form
you created in FormBackend (you can find the url by visiting the “Setup”-tab).
Go ahead and visit the contact page you just created. Fill out the form and click the “Send message” button. You’re not presented with a “Thank you for your submission” page and you should be able to see the submission under submissions for your form in FormBackend.
Let’s go ahead and make the experience a little nicer. We’re going to add a bit of
JavaScript and change how we handle the form state. Let’s start by adding some new
useState
variables at the top of your page code just below the export default ...
line:
export default function Contact() { const [formData, setFormData] = useState({ name: "", email: "", message: "" });
One thing to keep in mind here is: We want the name
, email
and message
attributes
in the state to match the name
tag of each field we added to the form. If we don’t do this
it won’t work!
Now we have a place to temporarily store the values that people enter in to your form before we submit it to FormBackend.
We need to trigger a function that does this when people enter data in the respective form fields.
On each of the three form fields, let’s add a onChange
callback. Let’s look at the name
field
as an example:
<input type="text" name="name" onChange="handleInput" />
Make sure you add onChange
to the email and message fields as well!
Time to write out our handleInput
function:
const handleInput = (e) => { const fieldName = e.target.name; const fieldValue = e.target.value; setFormData((prevState) => ({ ...prevState, [fieldName]: fieldValue })); }
Now every time someone writes something in the form fields, it updates the state representing the form data.
But how do we submit it to FormBackend you’re asking?
We need to add another callback to the form. This time it’s the onSubmit
callback:
<form method="POST" action="[your-unique-url]" onSubmit={submitForm}>
Let’s write out 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: "" }) }) }
Let us take a closer look at what’s going on in the above code.
The e.preventDefault()
line, tells the form to not do it’s default behavior which is to submit and refresh the page.
We then store the value of the action
attribute on the form (the FormBackend URL) in the formURL variable
.
We need a way to take our formData
state and turn that in to a proper FormData
object that the browser can use when submitting
the form to FormBackend. We do that by generating a new FormData()
object and assigning that to the data
variable. We then iterate over
all the values in our formData
state object and append that to the FormData
assigned to data
.
Now it’s time to submit the form, for that we’re going to use fetch
which is in all modern browsers. We set the method
to POST
as
that is what FormBackend expects (and the right way to send 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. We need to add one small thing for this to work properly. For each of our fields we want to bind the value
to their respective
value in our formData
state object. 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>
Now when we submit the form, we send all the data to FormBackend, we reset our formData
values and you’ll see the form be reset to all empty values again.
It can be a little jarring to submit a form, and see all the fields reset without any confirmation of what just happened. So let’s display a small message on the page and hide the form once it has been submitted successfully.
In order to do so we need to introduce a few extra things in our Next.js contact form. First let’s introduce a new variable named formSuccess
using useState
. Towards the top of the file
add the following:
const [formSuccess, setFormSuccess] = useState(false)
This new state will keep track of if the Next.js 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 now add a bit of logic to our HTML by using the conditional (ternary) operator:
return ( <div> <h1>Contact form</h1> {formSuccess ? <div>Form submitted</div> : <form method="POST" action="https://www.formbackend.com/f/664decaabbf1c319" onSubmit={submitForm}> <div> <label>Name</label> <input type="text" name="name" onChange={handleInput} value={formData.name} /> </div> <div> <label>Email</label> <input type="text" name="email" onChange={handleInput} value={formData.email} /> </div> <div> <label>Message</label> <textarea name="message" onChange={handleInput} value={formData.message}></textarea> </div> <button type="submit">Send message</button> </form> } </div> )
Notice towards the top how we check if formSucess
is true and if it is we display Form submitted
if not, we display the form. That’ll make it so when you submit the form
we hide the form itself and show Form submitted
.
We can make this even more dynamic and use the submittion text returned by the server. Make the following change around the then()
line:
}).then((response) => response.json()) .then((data) => {
We will now parse the server response as JSON
which we can access via the data
attribute. Let’s introduce a new piece of state
which we’ll call formSuccessMessage
. Towards the top
of the file we’ll add:
const [formSuccessMessage, setFormSuccessMessage] = useState("")
We will then change our “Form submitted” message to <div>{formSuccessMessage}
and below our setFormData
reset in the then
callback for fetch
- we’ll add:
setFormSuccessMessage(data.submission_text)
The JSON returned by FormBackend when you submit the Next.js form using JavaScript contains the following:
{ 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 contact.js
Next.js page looks like this:
import React, { useState } from "react" export default function Contact() { 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 ( <div> <h1>Contact form</h1> {formSuccess ? <div>{formSuccessMessage}</div> : <form method="POST" action="https://www.formbackend.com/f/664decaabbf1c319" onSubmit={submitForm}> <div> <label>Name</label> <input type="text" name="name" onChange={handleInput} value={formData.name} /> </div> <div> <label>Email</label> <input type="text" name="email" onChange={handleInput} value={formData.email} /> </div> <div> <label>Message</label> <textarea name="message" onChange={handleInput} value={formData.message}></textarea> </div> <button type="submit">Send message</button> </form> } </div> ) }
That is how you create a simple contact form for Next.js and gradually make it more advanced using FormBackend. The code can be found on GitHub. Please reach out to us if you have any questions.