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).
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.
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
or
yarn create next-app --typescript
then add these packages,
yarn add react-google-recaptcha-v3
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;
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;
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!!!",
});
}
}
Top comments (1)
Thank you for this article