• Homeright arrow
  • Blogright arrow
  • How to Add Google reCAPTCHA to a form in Next.js
Feature

How to Add Google reCAPTCHA to a form in Next.js

Introduction

Online data security and privacy have become the main concerns in today’s Digital world. It is very important to make sure that the forms we use on a website for various reasons are safe. By using bots in a form, cybercriminals usually exploit data from a website. That’s why to protect the websites, an extra layer of protection is needed. To solve the problem Google comes with Google ReCaptcha.  In this article, we will explore how to add Google ReCaptcha to a Next Js project.

What is Google ReCaptcha?

Google reCAPTCHA is a service designed to protect websites from spam and abuse by distinguishing between human users and automated bots. It helps secure websites by ensuring that interactions such as form submissions, account logins, or even viewing content are performed by real people, not malicious programs. There are two types of Google Recaptcha. These are:

  • reCAPTCHA v2 ("I am not a robot" Checkbox).
  • reCAPTCHA v3 (Invisible, no user interaction).

In this article, we will add Google reCAPTCHA v2.

Step 1: Create a Google reCAPTCHA Account

To use Google Recaptcha in our Next Js project, you’ll need to create reCAPTCHA API keys from Google.

  • Visit the Google reCAPTCHA admin console and sign in with your Google account.
  • Once logged in, you'll be directed to the "Register a new site" form.
  • Label: Give your reCAPTCHA configuration a name (e.g., your website’s name).
  • reCAPTCHA Type: Choose the reCAPTCHA version you'd like to use

reCAPTCHA v2:

  1. “I’m not a robot” Checkbox
  2. Invisible reCAPTCHA Badge
  3. reCAPTCHA v2 Android

reCAPTCHA v3: Provides a score based on interactions and doesn’t require user interaction.

  • Domains: Enter the domain names (e.g., localhost ) on which you want to use reCAPTCHA.
  • Owners: Add email addresses of the owners (default is your email).
  • Click Submit to register on the site.
  • After submission, you will be provided with:

Site key: Used in the frontend (HTML) to render the reCAPTCHA widget.

Secret key: Used in the backend to validate the reCAPTCHA response.

Step 2: Add reCAPTCHA to Your Frontend

Prepare Your Next.js Application

Before setting up the GitHub Actions workflow, the first and foremost step is to create a Next.js App. Use the following command to create a next.js app.

npx create-next-app@latest app-name

As it is not an article on creating the next.js app, we will not discuss it in detail. If you want to read it in detail follow the link.

Install Dependencies

In your Next.js project directory, install the reCAPTCHA package:

npm install react-google-recaptcha

Create .env file

First, create a .env file at the root of the project. In this file, store the Google reCAPTCHA site key and Secret Key.

NEXT_PUBLIC_RECAPTCHA_SITE_KEY=XXXXXXXXXXX
RECAPTCHA_SECRET_KEY=XXXXXXXXXXX

Verify reCAPTCHA

To process HTTP POST requests, you must create an asynchronous POST handler in a JavaScript function. We will use Axios for this. So, install Axios using the following commands:

npm install axios

Now create an api folder inside the app folder. Then create a route.js file.  This file is designed to verify a reCAPTCHA token you have added. Finally, add the following codes:

import axios from "axios";
export async function POST(req) {
if (req.method !== "POST") {
return new Response(JSON.stringify({message: "Only POST requests allowed"}), {status: 405});
}

const data = await req.json();
const {token} = data;
const secretKey = process.env.RECAPTCHA_SECRET_KEY;

if (!token) {
return new Response(JSON.stringify({message: "Token not found"}), {
status: 405,
});
}

try {
const response = await axios.post(
`https://www.google.com/recaptcha/api/siteverify?secret=${secretKey}&response=${token}`
);


if (response.data.success) {
return new Response(JSON.stringify({message: "Success"}), {
status: 200,
});
} else {
return new Response(JSON.stringify({message: "Failed to verify"}), {
status: 405,
});
}
} catch (error) {
return new Response(JSON.stringify({message: "Internal Server Error"}), {
status: 500,
});
}
}

In this example:

  • The function begins by checking if the request method is POST. If it's not, it responds with a 405 status code (Method Not Allowed) and a message: "Only POST requests allowed".
  • The function extracts the JSON data from the incoming request using req.json() and expects the token (reCAPTCHA token) to be included in the body.
  • The reCAPTCHA secret key (RECAPTCHA_SECRET_KEY) is retrieved from the environment variables (process.env.RECAPTCHA_SECRET_KEY).
  • If the token is not found in the request, the function immediately returns a response with a 405 status and a message: "Token not found".
  • If a token is found, it uses axios.post() to send a POST request to Google's reCAPTCHA verification API (https://www.google.com/recaptcha/api/siteverify), passing the secretKey and token as query parameters.
  • Google’s API will respond with whether the verification was successful or not.
  • If Google’s API returns a successful response (response.data.success === true), the function responds with a 200 status and a message: "Success".
  • If the verification fails, it responds with a 405 status and a message: "Failed to verify".
  • If there is an error during the process (e.g., the external API call fails), the function catches the error and responds with a 500 status and a message: "Internal Server Error".

Create a submitForm Route

Let’s create a sample submitForm route to handle the form submission. You can use it to process the form data later according to your needs. To do that, inside the API folder, create a submitForm folder. Inside it now, create a route.js file and add the following codes:

// /app/api/submitForm/route.js


export async function POST(req) {
const {name, email, message} = await req.json();


if (!name || !email || !message) {
return new Response(JSON.stringify({message: "Missing fields"}), {
status: 400,
});
}


try {
// Here you can process the form data, such as sending an email or saving to a database
// For example, send an email or save the data to a database.


return new Response(JSON.stringify({message: "Form submitted successfully!"}), {
status: 200,
});
} catch (error) {
return new Response(JSON.stringify({message: "Internal Server Error"}), {
status: 500,
});
}
}

These codes handle form submissions by receiving a JSON payload, validate that all required fields are present, Handle errors during the processing of form data and Send appropriate HTTP responses based on the result of the operation (either success, bad request, or server error).

Create A hook to use it in reCAPTCHA

Create a utils folder at the root of the project. Now add a useCaptcha.js file. This file will define a custom hook useCaptcha that manages the state and behavior of a CAPTCHA verification process Inside the file add the following codes:

import {useState, useRef} from "react";


export const useCaptcha = () => {
const recaptchaRef = useRef(null);
const [isVerified, setIsVerified] = useState(false);
const [captchaMessage, setCaptchaMessage] = useState("");


const handleCaptchaSubmission = async (token) => {
try {
if (token) {
const response = await fetch("/api", {
method: "POST",
headers: {
Accept: "application/json",
"Content-Type": "application/json",
},
body: JSON.stringify({token}),
});


if (response.ok) {
setIsVerified(true);
setCaptchaMessage("CAPTCHA verified successfully!"); // Set success message
} else {
setIsVerified(false);
const data = await response.json();
setCaptchaMessage(`CAPTCHA verification failed: ${data.message}`);
}
}
} catch (e) {
setIsVerified(false);
setCaptchaMessage("CAPTCHA verification failed due to an error.");
}
};


const handleExpired = () => {
setIsVerified(false);
setCaptchaMessage("CAPTCHA expired, please try again.");
};


return {
recaptchaRef,
isVerified,
handleCaptchaSubmission,
handleExpired,
};
};

In this example, handleCaptchaSubmission(token), an asynchronous function handles the CAPTCHA token submission to the server. Finally returns an object with recaptchaRef, isVerified, handleCaptchaSubmission and handleExpired. 

Create a useSubmitForm hook

Create another file in the utils folder and name it  useSubmitForm.js file. Now create two components. One is for the contact form and another is for the form submission message. Now add the following codes:

import {useState} from "react";


export const useSubmitForm = () => {
const [message, setMessage] = useState("");


const handleSubmit = async (e, isVerified) => {
e.preventDefault();


const formData = new FormData(e.target);
const name = formData.get("name");
const email = formData.get("email");
const messageText = formData.get("message");


// Log the input data to the console
console.log("Form Data:", {
name,
email,
message: messageText,
});


if (!isVerified) {
setMessage("Please complete the CAPTCHA first.");
return;
}


try {
const response = await fetch("/api/submitForm", {
method: "POST",
headers: {
Accept: "application/json",
"Content-Type": "application/json",
},
body: JSON.stringify({name, email, message: messageText}),
});


if (response.ok) {
setMessage("Form submitted successfully!");
e.target.reset(); // Reset the form
} else {
setMessage("Failed to submit the form. Please try again.");
}
} catch (error) {
setMessage("An error occurred. Please try again later.");
}
};


return {
message,
setMessage,
handleSubmit,
};
};

This code will manage the submission of a form. A handleSubmit Function is called when the form is submitted. It prevents the default form submission behaviour (e.preventDefault()), extracts form data, checks if a CAPTCHA verification is completed (isVerified), and sends the form data to a backend API. Then The hook returns the message state, setMessage (for manually setting messages if needed), and the handleSubmit function. These can be used in any component that calls this hook.

Create Components

All our routes and hooks have been created, let’s create our form components. Create a components folder and add the ContactForm.jsx file. Now add the following codes:

"use client";
import ReCAPTCHA from "react-google-recaptcha";
import {useCaptcha} from "@/utils/useCaptcha";
import {useSubmitForm} from "@/utils/useSubmitForm";


const ContactForm = () => {
const {recaptchaRef, isVerified, handleCaptchaSubmission, handleExpired} = useCaptcha();
const {message, handleSubmit} = useSubmitForm();


return (
<div className="flex flex-col items-center gap-3">
<ReCAPTCHA
sitekey={process.env.NEXT_PUBLIC_RECAPTCHA_SITE_KEY || ""}
ref={recaptchaRef}
onChange={handleCaptchaSubmission}
onExpired={handleExpired}
/>
<form
className="flex items-center flex-col gap-3"
onSubmit={(e) => handleSubmit(e, isVerified)}
>
<input
type="name"
placeholder="Name"
name="name"
className="p-2.5 text-lg w-full rounded-md focus:ring-2 focus:ring-blue-300 bg-gray-50 border border-gray-300 text-gray-900 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500 focus:outline-none"
/>
<input
type="email"
placeholder="Email"
name="email"
className="p-2.5 text-lg w-full rounded-md focus:ring-2 focus:ring-blue-300 bg-gray-50 border border-gray-300 text-gray-900 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500 focus:outline-none"
/>
<textarea
placeholder="Message"
name="message"
className="p-2.5 text-lg w-full rounded-md focus:ring-2 focus:ring-blue-300 bg-gray-50 border border-gray-300 text-gray-900 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500 focus:outline-none"
></textarea>
<button
className="border-solid border-1 border-gray-300 rounded-md p-2 bg-blue-500 text-white disabled:bg-gray-300 disabled:text-gray-500 disabled:cursor-not-allowed"
type="submit"
disabled={!isVerified}
>
Submit Form
</button>
</form>
{message && <p className="text-lg text-green-500">{message}</p>}
</div>
);
};
export default ContactForm;

This component integrates a simple form with Google reCAPTCHA verification. The form includes input fields for a name, email, and message, ensuring that the user completes the CAPTCHA verification before submitting the form. 

  • We have imported useSubmitForm and useCaptcha. Then we have destructured values from two hooks to manage CAPTCHA verification and form submission. It ensures that the form submission logic (handleSubmit) only proceeds if the CAPTCHA is successfully verified (isVerified), and it provides feedback (message) based on the result of the form submission.
  • We have also imported ReCAPTCHA from react-google-recaptcha. Then pass the data from the useCaptcha hook. this ReCAPTCHA component:
  • Renders a Google reCAPTCHA using a site key from environment variables.
  • Uses a ref (recaptchaRef) to manage the CAPTCHA state programmatically.
  • Executes handleCaptchaSubmission when the CAPTCHA is solved successfully.
  • Executes handleExpired when the CAPTCHA expires, resetting the verification status.
  • It ensures that the user successfully completes the CAPTCHA before proceeding with any form submissions.
  • Other codes are an example of a simple contact form component where we use handleSubmit() to submit the form.

Use the Contact Form Components

We are almost done. Now you can use this ContactForm component whenever you wish. For testing purposes, we will add it in the app/page.js file. Remove all the codes from this file and add the following codes:


import ContactForm from "@/components/ContactForm";
const Page = () => {
return (
<main className="flex flex-col items-center mt-10 gap-3">
<h2 className="text-2xl font-semibold">Contact Us</h2>
<ContactForm />
</main>
);
};


export default Page;

We have just imported the ContactForm component and used it on our page.

Let’s test what we have got so far. Run the site using  the following command:

npm run dev

It will open the site in localhost:3000. A Google recaptcha should be shown with a simple form field. At first, the form button will be deactivated. After successfully submitting the captcha, it will be activated only.

Congratulations! You have successfully added Google reCaptcha as well as added an extra layer of protection on our website.

Conclusion

Successfully adding Google reCAPTCHA to your Next.js site means you’ve fortified your defences against spam and automated abuse. With reCAPTCHA, you can ensure that your forms are only accessible to legitimate users, providing a seamless and secure experience for everyone. Happy coding!



Keep exploring and enhancing your Next.js projects! Please do not hesitate to contact StaticMania with any questions or feedback. Happy coding!

footer-particlefooter-particlefooter-particlefooter-particlefooter-particle
back-to-top