How to create a Contact Form with Recaptcha hosted on Netlify

codetraveling

Lars

Posted on December 29, 2020

How to create a Contact Form with Recaptcha hosted on Netlify

*This post was first published on my blog.

One of the many handy features of hosting your website on Netlify is the option to have form submissions from your website showing right up in your Netlify dashboard. Or once activated, you can even have email notifications.

With the free tier on Netlify, this is limited to 100 submissions per site/month, but for most of my clients, this is more than enough. However, I have always found the Netlify Docs to this topic quite confusing, and especially the reCaptcha by Netlify hasn't worked for me.

So in this tutorial, we will go through the process of implementing a simple contact form with our own Google reCaptcha on Netlify.

Let's get started!

*As this tutorial is a step by step guide, I recommend you to go through it that way to understand the details, but if you don't mind, you can also find the whole code at the end of this article! This example uses Gatsby and "react-google-recaptcha"

Table Of Contents

Getting started with a basic contact form

To get you started, you will need a basic contact form with a name, email, and message field. And React state will preserve the user's input on change. Notice that in our handleSubmit function we use Gatsby's navigate function to redirect the user to /thank-you. This is the only Gatsby related thing in this example.

import React from 'react'
import { navigate } from 'gatsby'

const ContactForm = () => {
  const [state, setState] = React.useState({})

  const handleChange = e => {
    setState({ ...state, [e.target.name]: e.target.value })
  }

  const handleSubmit = e => {
    e.preventDefault()
    const form = e.target

    fetch('/', {
      method: 'POST',
      headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
      body: encode({
        'form-name': form.getAttribute('name'),
        ...state,
      }),
    })
      .then(() => navigate(form.getAttribute('action')))
      .catch(error => alert(error))
  }

  return (
    <form
      name="contact-us"
      method="POST"
      action="/thank-you"
      onSubmit={handleSubmit}
    >
      <noscript>
        <p>This form won’t work with Javascript disabled</p>
      </noscript>
      <div>
        <input
          type="text"
          id="text-input"
          name="name"
          onChange={handleChange}
          required
        />
        <label htmlFor="text-input">Name / Alias</label>
      </div>
      <div>
        <input
          id="email-input"
          type="email"
          name="email"
          placeholder=""
          onChange={handleChange}
          required
        />
        <label htmlFor="email-input">Email</label>
      </div>
      <div>
        <textarea
          id="textarea"
          type="text"
          name="message"
          onChange={handleChange}
          required
        />
        <label htmlFor="textarea">Message</label>
      </div>
      <buttonn type="submit">Submit</button>
    </form>
  )
}

export default ContactForm
Enter fullscreen mode Exit fullscreen mode

Add the Netlify data attribute to your form element

Now for the contact form to show in your Netlify dashboard, you will have to include the Netlify data attribute. This way Netlify can know during deployment that there is a form it needs to pay attention to.

// ... imports here

const ContactForm = () => {

    // ... all the previous code

  return (
    <form
      name="contact-us"
      method="POST"
      data-netlify="true"              // the Netlify data attribute
      action="/thank-you"
      onSubmit={handleSubmit}
    >
         {/* ... all the inputs and submit button here */}
    </form>
  )
}

export default ContactForm
Enter fullscreen mode Exit fullscreen mode

Get your own reCaptcha key

Before we get to implement our reCaptcha component in our contact form, we will have to get our free Recaptcha key from Google. Conveniently, as usual, anybody with a Google account can create one here.

Get your own reCaptcha Key

Note here: Make sure to check reCaptcha v2 and "I'm not a robot" tickbox, as this is what we use in our example.

If you want to use the invisible reCaptcha badge, you can find the documentation here, or for reCaptcha v3 you can check out react-google-recaptcha-v3.

Once you have registered, you will see your pair of reCaptcha keys:

Your own reCaptcha keys

In our case, we will only need the SITE KEY!

So go ahead and copy your key. And since it is a public key, we can safely create a variable in our component like so:

// ... imports here

const RECAPTCHA_KEY = '6LdGSewdkljsdakldasdklj3e2asddaDDS9083'

const ContactForm = () => {
  // ... previous code here

    return (
        // ... previous code here
    )
}

export default ContactForm
Enter fullscreen mode Exit fullscreen mode

Add "react-google-recaptcha"

With our RECAPTCHA_KEY at hand, we can now install the react-google-recaptcha package, which will handle most of the reCaptcha stuff for us.

yarn add react-google-recaptcha
Enter fullscreen mode Exit fullscreen mode

or

npm install react-google-recaptcha
Enter fullscreen mode Exit fullscreen mode

But to ensure that the reCaptcha talks to our contact form and submissions won't be submitted without reCaptcha's approval, we have to write a little more code on our own.

I'll give you the pieces of code first and afterward, you will find the example with the new parts implemented.

Our Recaptcha component:

import Recaptcha from 'react-google-recaptcha'

// ... stuff in between

return (
    // ... stuff
    <Recaptcha
      ref={recaptchaRef}
      sitekey={RECAPTCHA_KEY}
      size="normal"
      id="recaptcha-google"
    />
)
Enter fullscreen mode Exit fullscreen mode

Netlify Recatpcha data attribute:

data-netlify-recaptcha="true"   // new Netlify data attribute
Enter fullscreen mode Exit fullscreen mode

The recaptchaRef:

const recaptchaRef = React.createRef() // new Ref for reCaptcha
Enter fullscreen mode Exit fullscreen mode

The new handleSubmit function including reCaptchaValue.

const handleSubmit = e => {
  e.preventDefault()
  const form = e.target
  const recaptchaValue = recaptchaRef.current.getValue()

  fetch('/', {
    method: 'POST',
    headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
    body: encode({
      'form-name': form.getAttribute('name'),
      'g-recaptcha-response': recaptchaValue,
      ...state,
    }),
  })
  .then(() => navigate(form.getAttribute('action')))
  .catch(error => alert(error))
}
Enter fullscreen mode Exit fullscreen mode

And here the whole example:

// ... other imports here
import Recaptcha from 'react-google-recaptcha'

const RECAPTCHA_KEY = '6LdGSewdkljsdakldasdkljasdda'

const ContactForm = () => {
  const [state, setState] = React.useState({})

  const recaptchaRef = React.createRef() // new Ref for reCaptcha

  const handleChange = e => ...

  const handleSubmit = e => {
    e.preventDefault()
    const form = e.target
    const recaptchaValue = recaptchaRef.current.getValue()

    fetch('/', {
      method: 'POST',
      headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
      body: encode({
        'form-name': form.getAttribute('name'),
        'g-recaptcha-response': recaptchaValue,
        ...state,
      }),
    })
    .then(() => navigate(form.getAttribute('action')))
    .catch(error => alert(error))
  }

  return (
    <form
      name="contact-us"
      method="POST"
      data-netlify="true"
      data-netlify-recaptcha="true"   // new Netlify data attribute regarding recaptcha submissions
      action="/thank-you"
      onSubmit={handleSubmit}
    >
      {/* ... all the other inputs here */}

      <Recaptcha
        ref={recaptchaRef}
        sitekey={RECAPTCHA_KEY}
        size="normal"
        id="recaptcha-google"
      />
      <button type="submit">Submit</button>
    </form>
  )
}

export default ContactForm
Enter fullscreen mode Exit fullscreen mode

Don't forget to disable submit unless reCaptcha is approved

Now after I had come to this point, I noticed that even without submitting reCaptcha, the user would be able to press the submit button and get past the check. This was no good. So I need to ensure that the button was disabled as long as the user hadn't pressed the reCaptcha check.
Luckily react-google-recaptcha provides us with an onChange function that allows you to set a React state for the submit button, like so:

// ... imports here

const ContactForm = () => {
    // ... other React State and Ref

    // With this the button is disabled by default, but on Recaptcha change, the form can be submitted
  const [buttonDisabled, setButtonDisabled] = React.useState(true)

  // ... previous code

  return (
    <form  {/* ... attributes here */}>
      <Recaptcha
        ref={recaptchaRef}
        sitekey={RECAPTCHA_KEY}
        size="normal"
        id="recaptcha-google"
        onChange={() => setButtonDisabled(false)} // disable the disabled button!
      />
      <button type="submit" disabled={buttonDisabled}>Submit</button>
    </form>
  )
}

export default ContactForm
Enter fullscreen mode Exit fullscreen mode

Now on top of this, you might want to consider adding some CSS to let the user know when the button is disabled:

button[disabled] {
  cursor: not-allowed;
}
Enter fullscreen mode Exit fullscreen mode

Add email notifications

Last, but not least, we probably don't want to randomly check our Netlify dashboard for submissions, but instead, get automatic email notifications.

For this login to your Netlify dashboard, and in your site settings under Form and Form notification, you can set your preferences.

Netlify Email notifications

You can find below the final code and with some styling, you have yourself a free and safe, working contact form on Netlify!

Final result:

import React from 'react'
import { navigate } from 'gatsby'
import Recaptcha from 'react-google-recaptcha'

const RECAPTCHA_KEY = '6LdGSewdkljsdakldasdkljasdda'

const ContactForm = () => {
  const [state, setState] = React.useState({})
  const recaptchaRef = React.createRef()
  const [buttonDisabled, setButtonDisabled] = React.useState(true)

  const handleChange = e => {
    setState({ ...state, [e.target.name]: e.target.value })
  }

  const handleSubmit = e => {
    e.preventDefault()
    const form = e.target
    const recaptchaValue = recaptchaRef.current.getValue()

    fetch('/', {
      method: 'POST',
      headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
      body: encode({
        'form-name': form.getAttribute('name'),
        'g-recaptcha-response': recaptchaValue,
        ...state,
      }),
    })
    .then(() => navigate(form.getAttribute('action')))
    .catch(error => alert(error))
  }

  return (
    <form
      name="contact-us"
      method="POST"
      data-netlify="true"
      data-netlify-recaptcha="true"
      action="/thank-you"
      onSubmit={handleSubmit}
    >
      <noscript>
        <p>This form won’t work with Javascript disabled</p>
      </noscript>
      <div>
        <input
          type="text"
          id="text-input"
          name="name"
          onChange={handleChange}
          required
        />
        <label htmlFor="text-input">Name / Alias</label>
      </div>
      <div>
        <input
          id="email-input"
          type="email"
          name="email"
          placeholder=""
          onChange={handleChange}
          required
        />
        <label htmlFor="email-input">Email</label>
      </div>
      <div>
        <textarea
          id="textarea"
          type="text"
          name="message"
          onChange={handleChange}
          required
        />
        <label htmlFor="textarea">Message</label>
      </div>
      {/* Recaptcha */}
      <Recaptcha
        ref={recaptchaRef}
        sitekey={RECAPTCHA_KEY}
        size="normal"
        id="recaptcha-google"
        onChange={() => setButtonDisabled(false)}
      />
      <button type="submit" disabled={buttonDisabled}>Submit</button>
    </form>
  )
}

export default ContactForm
Enter fullscreen mode Exit fullscreen mode

That’s pretty much it!

Thanks so much for reading this far and feel free to reach out to me anytime, on my website or Twitter 🙂 And if you like to read more, make sure to check out my other posts on my blog!

💖 💪 🙅 🚩
codetraveling
Lars

Posted on December 29, 2020

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related