Integrating reCAPTCHA with Next.js
Tags: nextjs, react, javascript, programming
Published: 2022-06-21 20:18:05


In this post, I will demonstrate how to incorporate reCAPTCHA into Next.js applications.

But, before we get started, let's first grasp how reCAPTCHA works. Assume you built a portfolio website with a contact us form, but instead of receiving actual messages, you received a lot of spam; those spam messages were generated by bots; to block bots, we will utilize reCAPTCHA.



Let's put it into action now. First, you go to this website and fill out the form with the relevant information (shown below).
![Google reCAPTCHA admin console](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/f7yijspw8jhqqp8ke828.png)
 

After submitting the form, you will be given a site key (which the browser can see) and a secret key; add those into .env.local as seen below.
![Form values](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/p5wlk8cq7ap284eybytp.png)
 

---
## We will now begin integrating reCAPTCHA with Next.js 12
---
> #### Project Repo(This is personal portfolio website):- https://github.com/Sumukha210/new-portfolio-website

---
### Installing Next.js along with other dependencies,
  
``` bash
npx create-next-app@latest --typescript
```
#### or

``` shell
yarn create next-app --typescript 
```
then add these packages,
``` bash
yarn add  react-google-recaptcha-v3
```
Add this code to **_app.tsx(or .jsx)** file. Here we are wrapping our Component with GoogleRecaptchaProvider.

```tsx
import type { AppProps } from "next/app";
import "@/styles/styles.scss";
import { GoogleReCaptchaProvider } from "react-google-recaptcha-v3";

function MyApp({ Component, pageProps }: AppProps) {
  return (
    <GoogleReCaptchaProvider
      reCaptchaKey={process.env.NEXT_PUBLIC_RECAPTHA_SITE_KEY}
      scriptProps={{
        async: false, // optional, default to false,
        defer: true, // optional, default to false
        appendTo: "body", // optional, default to "head", can be "head" or "body",
        nonce: undefined,
      }}>
      <Component {...pageProps} />;
    </GoogleReCaptchaProvider>
  );
}

export default MyApp;
```
Now, add this code onto the page where you've placed the form. Here, I first imported the useGoogleRecaptcha hook, and once the form is verified, I check if reCAPTCHA is loaded or not, and if it is, I return from the function. If there is no error, I send the token along with other data to the server.

```tsx
import React, { useState } from "react";
import { useGoogleReCaptcha } from "react-google-recaptcha-v3";
import { Wrapper, SubmitBtn } from "./RightSectionStyles";
import axios from "axios";
import { responseTypes } from "@/utils/types";

const RightSection = () => {
  const [isBtnDisabled, setBtnDisabled] = useState(false);
  const { executeRecaptcha } = useGoogleReCaptcha();
  const [response, setResponse] = useState<responseTypes | null>(null);

  const handleSubmit = async (e: any) => {
    e.preventDefault();
    const name = e.target[0];
    const email = e.target[1];
    const message = e.target[2];

    [name, email, message].forEach(item => {
      if (item.validity.valid === false && item.validity.valid) {
        return;
      }
    });

    setBtnDisabled(true);

    if (!executeRecaptcha) {
      return;
    }

    try {
      const token = await executeRecaptcha();
      if (!token) {
        setResponse({ message: "Failed to Send!!!", status: "Failed" });
        return;
      }

      const result = await axios.post("/api/contactUs", {
        token,
        name: name.value,
        email: email.value,
        message: message.value,
      });
      console.log(result);

      if (result.data) {
        setResponse({
          message: result.data.message,
          status: result.data.status,
        });
      }
      setBtnDisabled(false);
    } catch (error) {
      console.log(error);
      setResponse({ message: "Failed to Send!!!", status: "Failed" });
      setBtnDisabled(false);
    }
  };

  return (
    <Wrapper>
      <form onSubmit={handleSubmit}>
        <div className="flex">
          <section>
            <input
              type="text"
              placeholder="Enter your name"
              name="name"
              required
            />
          </section>

          <section>
            <input
              type="email"
              placeholder="Enter your email"
              name="email"
              required
            />
          </section>
        </div>

        <section>
          <textarea
            name="message"
            placeholder="Enter your message"
            cols={30}
            rows={10}></textarea>
        </section>

        <div className="responseText">
          <h3
            className="subtitle-4"
            style={{
              color: response?.status === "Failed" ? "red" : "#24ff72",
            }}>
            {response?.message}
          </h3>
        </div>

        <div className="btnContainer">
          <SubmitBtn disabled={isBtnDisabled} type="submit" marginTop="2rem">
            <span>Submit{isBtnDisabled && "ting"}</span>
            {isBtnDisabled && <span className="loader"></span>}
          </SubmitBtn>
        </div>
      </form>
    </Wrapper>
  );
};

export default RightSection;
```
In **/pages/api**, create a new file(endpoint) **contactUs.ts** and add this code.

```tsx
import { responseTypes } from "@/utils/types";
import axios from "axios";
import type { NextApiRequest, NextApiResponse } from "next";

const verifyRecaptcha = async token => {
  const secretKey = process.env.RECAPTHA_SECRET_KEY;

  var verificationUrl =
    "https://www.google.com/recaptcha/api/siteverify?secret=" +
    secretKey +
    "&response=" +
    token;

  return await axios.post(verificationUrl);
};

export default async function handler(
  req: NextApiRequest,
  res: NextApiResponse<responseTypes>
) {
  try {
    const name = req.body.name;
    const email = req.body.email;
    const message = req.body.message;
    const token = req.body.token;

    // Recaptcha response
    const response = await verifyRecaptcha(token);

    // Checking if the reponse sent by reCaptcha success or not and if the score is above 0.5
    // In ReCaptcha v3, a score sent which tells if the data sent from front end is from Human or from Bots. If score above 0.5 then it is human otherwise it is bot
    // For more info check, https://developers.google.com/recaptcha/docs/v3
    // ReCaptcha v3 response, {
    //     "success": true|false,      // whether this request was a valid reCAPTCHA token for your site
    //     "score": number             // the score for this request (0.0 - 1.0)
    //     "action": string            // the action name for this request (important to verify)
    //     "challenge_ts": timestamp,  // timestamp of the challenge load (ISO format yyyy-MM-dd'T'HH:mm:ssZZ)
    //     "hostname": string,         // the hostname of the site where the reCAPTCHA was solved
    //     "error-codes": [...]        // optional
    //   }
    if (response.data.success && response.data.score >= 0.5) {
      return res
        .status(200)
        .json({ status: "Success", message: "Thank you for contacting me." });
    } else {
      return res.json({
        status: "Failed",
        message: "Something went wrong, please try again!!!",
      });
    }
  } catch (error) {
    console.log("ERRRRROr", error);
    res.json({
      status: "Failed",
      message: "Something went wrong, please try again!!!",
    });
  }
}

```