DEV Community

Cover image for Integrating reCAPTCHA with Next.js
Sumukhakb
Sumukhakb

Posted on • Updated on

Integrating reCAPTCHA with Next.js

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

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


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,

npx create-next-app@latest --typescript
Enter fullscreen mode Exit fullscreen mode

or

yarn create next-app --typescript 
Enter fullscreen mode Exit fullscreen mode

then add these packages,

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

Add this code to _app.tsx(or .jsx) file. Here we are wrapping our Component with GoogleRecaptchaProvider.

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;
Enter fullscreen mode Exit fullscreen mode

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.

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;
Enter fullscreen mode Exit fullscreen mode

In /pages/api, create a new file(endpoint) contactUs.ts and add this code.

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!!!",
    });
  }
}

Enter fullscreen mode Exit fullscreen mode

Discussion (0)