Netlify Contact Form with React Ant Design Form Components

April 06, 2020TutorialCode

Using a Netlify form should be dead simple. The documentation isn't very thorough and they are doing a lot in the background that you usually don't need to know but when things go wrong it sure would have been nice to see some documentation.

Skip ahead

The full code and an interactive demo are at the bottom of the page.

What we are building

We're building a simple contact form with three fields — name, email, message.

It will be a React component that uses the Ant Design components to display a Netlify form.

The setup

React 16.13

GatsbyJS 2.19

Ant Design 4.1

ES6

I'm using Gatsby in this case but this tutorial doesn't refer to any Gatsby specifics. It should be fine for any project using Ant Design React Components and Netlify forms.

How does Netlify know about your form?

It's important to know how Netlify is working in the background to detect your form so we can give it exactly what it wants.

After you deploy your project to Netlify, they run post processing scripts to find any forms on your site. If they find one, the form will show up in the forms section of your Netlify account.

Important: Your form will will not work on localhost. It must be deployed to Netlify to receive submissions.

Create the component

In this example we are using functional React so let's define the component.

Create a new file called NetlifyContactForm.js

We're going to need to use the form name in a couple of places so lets define it as a constant. The form name will be used by Netlify to give your form a name in your Netlify account. If you change this name in the future, Netlify will create a new form with the new name.

At this time, we'll also add our imports and create a base for our layout with a row and column. This will center our form on the page.

import React from 'react'
import { Row, Col, Button, Form, Input } from 'antd'
const { TextArea } = Input
import { UserOutlined, MailOutlined } from '@ant-design/icons'

const NetlifyContactForm = () => {

    const formName = `contact`

    return (
      <Row
          justify="space-around"
      >
          <Col
              xs={22}
              sm={18}
              md={16}
              lg={8}
          >

            {/* TODO: add form here */}

          </Col>
      </Row>
  )
}
export default NetlifyContactForm

Add a shadow html form for Netlify

You might think, all I need to do is add a form with Ant Design components and give it the form name and it will all work fine. This will lead to much pain and suffering. Netlify will not recognize your form correctly.

Ant forms don't render the field name on the input itself and this causes problems when Netlify searches for your form. Instead of trying to hack around that, we will just provide a hidden html version of the form on the page. This is only for the Netlify bots to use so we've marked it as hidden.

Think of this as defining your form for Netlify. Notice that we set the form name to use the formName constant. This will tell Netlify to use that name when creating the form.

{/*
    This defines how your form is setup for the Netlify bots.
    Users will not see or interact with this form.
*/}
<form
    name={formName}
    data-netlify="true"
    data-netlify-honeypot="bot-field"
    hidden
>
    <input type="text" name="name" />
    <input type="email" name="email" />
    <textarea name="message"></textarea>
</form>

Add the Ant Design form

Next let's create the actual form that users will interact with using the Ant Design form components.

We have 4 fields defined in our form.

  • bot-field: hidden field for netlify-honepot to use.
  • Name
  • Email
  • Message

On each of the form items we also add "rules" for validation. Ant forms will automatically apply these rules when the use hits submit. Upon error, the fields will show the message you define.

You can't use the same form name for this form as you did on the html version.

You can give this form a throw away name because the html version of the form is the one that is actually used. Using the same name will cause errors on the Netlify side.

<Form
    name="cf"
    method="post"
    onFinish={handleSubmit}
    layout="vertical"
>
    {/* This is the hidden field that the netlify-honeypot uses. */}
    <Form.Item
        label="Don't fill this out"
        className={`hidden`}
        style={{ display: `none` }}
        name="bot-field"
    >
        <Input type={`hidden`} />
    </Form.Item>

    <Form.Item
        label="Name"
        rules={[{ required: true, message: `Please enter your name.` }]}
        name="name"
    >
        <Input
            placeholder="Name"
            prefix={<UserOutlined className="site-form-item-icon" />}
        />
    </Form.Item>

    <Form.Item
        label="Email"
        rules={[{ required: true, type: `email`, message: `Please enter your email.` }]}
        name="email"
    >
        <Input
            placeholder="Your Email"
            prefix={<MailOutlined className="site-form-item-icon" />}
        />
    </Form.Item>

    <Form.Item
        label="Message"
        rules={[{ required: true, message: `Please enter your message.` }]}
        name="message"
    >
        <TextArea
            placeholder="Your Message"
            rows={5}
        />
    </Form.Item>

    <Form.Item>
        <Button type="primary" htmlType="submit" disabled={false}>
            Send
        </Button>
    </Form.Item>
</Form>

Handle the submission

Create a handleSubmit function. We've already told the form above to use handleSubmit on the form's onFinish callback.

const handleSubmit = (values) => {
    if (values[`bot-field`] === undefined) {
        delete values[`bot-field`]
    }

    fetch(`/`, {
        method: `POST`,
        headers: { 'Content-Type': `application/x-www-form-urlencoded` },
        body: encode({
            'form-name': formName,
            ...values,
        }),
    })
        .then(() => showSuccess())
        .catch(error => showError(error))
}

A couple of important things are happening in this function.

If the bot-field is empty, we have to remove it. It's presence is enough for Netlify to treat it as if you are a bot. You could arguable add an else statement and just not even submit the form in the first place if the bot-field is not empty. It's up to you, for this example we'll just let it go.

It's important the the body includes the 'form-name' field and that uses the formName constant.

You'll also notice that we have to encode the body fields. To do this we'll add an 'encode' function to the top of our file.

function encode(data) {
    return Object.keys(data)
        .map(key => encodeURIComponent(key) + `=` + encodeURIComponent(data[key]))
        .join(`&`)
}

Lastly, handle the result

In the handleSubmit function, we call showSuccess() and showError().

You can do what you please with those methods. You'll probably want to show the user a message about whether the form submitted successfully or not. You might consider using the React state to hide the form after submission and show a success message. In the full code below we've added the functions with console logs to get you started.

Hope this helped. The full code and an interactive example is below.

Full code

import React from 'react'
import { Row, Col, Button, Form, Input } from 'antd'
const { TextArea } = Input
import { UserOutlined, MailOutlined } from '@ant-design/icons'

function encode(data) {
    return Object.keys(data)
        .map(key => encodeURIComponent(key) + `=` + encodeURIComponent(data[key]))
        .join(`&`)
}

const NetlifyContactForm = () => {
    const formName = `contact`

    const handleSubmit = (values) => {
        if (values[`bot-field`] === undefined) {
            delete values[`bot-field`]
        }

        fetch(`/`, {
            method: `POST`,
            headers: { 'Content-Type': `application/x-www-form-urlencoded` },
            body: encode({
                'form-name': formName,
                ...values,
            }),
        })
            .then(() => showSuccess())
            .catch(error => showError(error))
    }

    const showSuccess = () => {
        // TODO: Show a success message or navigate to a success page.
        console.log(`form submitted successfully`)
    }

    const showError = (error) => {
        // TODO: Show an error message to the user
        console.log(`There was an error submitting the form`)
        console.log(error)
    }

    return (
        <Row
            justify="space-around"
        >
            <Col
                xs={22}
                sm={18}
                md={16}
                lg={8}
            >

                {/*
                    This defines how your form is setup for the Netlify bots.
                    Users will not see or interact with this form.
                */}
                <form
                    name={formName}
                    data-netlify="true"
                    data-netlify-honeypot="bot-field"
                    hidden
                >
                    <input type="text" name="name" />
                    <input type="email" name="email" />
                    <textarea name="message"></textarea>
                </form>

                <Form
                    name="cf"
                    method="post"
                    onFinish={handleSubmit}
                    layout="vertical"
                >
                    {/* This is the hidden field that the netlify-honeypot uses. */}
                    <Form.Item
                        label="Don't fill this out"
                        className={`hidden`}
                        style={{ display: `none` }}
                        name="bot-field"
                    >
                        <Input type={`hidden`} />
                    </Form.Item>

                    <Form.Item
                        label="Name"
                        rules={[{ required: true, message: `Please enter your name.` }]}
                        name="name"
                    >
                        <Input
                            placeholder="Name"
                            prefix={<UserOutlined className="site-form-item-icon" />}
                        />
                    </Form.Item>

                    <Form.Item
                        label="Email"
                        rules={[{ required: true, type: `email`, message: `Please enter your email.` }]}
                        name="email"
                    >
                        <Input
                            placeholder="Your Email"
                            prefix={<MailOutlined className="site-form-item-icon" />}
                        />
                    </Form.Item>

                    <Form.Item
                        label="Message"
                        rules={[{ required: true, message: `Please enter your message.` }]}
                        name="message"
                    >
                        <TextArea
                            placeholder="Your Message"
                            rows={5}
                        />
                    </Form.Item>

                    <Form.Item>
                        <Button type="primary" htmlType="submit" disabled={false}>
                            Send
                        </Button>
                    </Form.Item>
                </Form>
            </Col>
        </Row>
    )
}
export default NetlifyContactForm
Full code for NetlifyContactForm.js

<NetlifyContactForm />

Here it is. A working example below

I did disable the request so it won't actually send.

Get new posts to your inbox
I'll send you an email when I publish a new post