Integrating Google reCAPTCHA v3 in a Next.js 14 Project

Adding Google reCAPTCHA in Next.js 14+ with App Router

Introduction

Google reCAPTCHA helps protect websites from bots and automated abuse. Google reCAPTCHA v3 helps you protect your application from bots by running in the background without interrupting user experience. This guide walks you through integrating reCAPTCHA with Next.js 14.

Install the Required Package

Start by installing the react-google-recaptcha-v3 package:

yarn add react-google-recaptcha-v3

Set Up Environment Variables

Create a .env.local file and add your reCAPTCHA site key. This is essential for securely handling reCAPTCHA on both the client and server sides.

NEXT_PUBLIC_RECAPTCHA_SITE_KEY=your_site_key
RECAPTCHA_SECRET_KEY=your_secret_key

Add reCAPTCHA v3 to Your Component

Import the useGoogleReCaptcha hook from "react-google-recaptcha-v3". In this setup, useGoogleReCaptcha provides the executeRecaptcha function that triggers reCAPTCHA v3 in the background and returns the token upon success.

I prefered to use the a custom hook for verification:

hooks/useReCaptcha.ts
'use client'
import { useGoogleReCaptcha } from "react-google-recaptcha-v3"
 
export const useReCaptcha = (key = 'form_submit') => {
  const { executeRecaptcha } = useGoogleReCaptcha()
 
  const verifyReCaptcha = async () => {
    if (!executeRecaptcha) {
      return false
    }
 
    const token = await executeRecaptcha(key)
 
    const response = await fetch('/api/v1/verify-recaptcha', {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json'
      },
      body: JSON.stringify({ token })
    })
 
    const data: reCaptchaData = await response.json()
 
    if (data.success) {
      return true
    } else {
      console.error('reCaptcha verification failed: ', reCaptchaError?.[data["error-codes"]])
      return false
    }
  }
 
  return { verifyReCaptcha }
}

You can declare the custom types for error and response as per listed in the official site. (You can read more from Google reCAPTCHA)

type reCaptchaError = "missing-input-secret" | "invalid-input-secret" | "missing-input-response" | "invalid-input-response" | "bad-request" | "timeout-or-duplicate"
 
type reCaptchaData = {
  success: true
  challenge_ts: string  // timestamp of the challenge load (ISO format yyyy-MM-dd'T'HH:mm:ssZZ)
  hostname: string
  action: string
} | {
  success: false
  "error-codes": reCaptchaError
}

You can also construct the object to map the actial meaning of the these error codes:

const reCaptchaError = {
  "bad-request": "The request is invalid or malformed.",
  "missing-input-secret": "The secret parameter is missing.",
  "missing-input-response": "The response parameter is missing.",
  "invalid-input-secret": "The secret parameter is invalid or malformed.",
  "invalid-input-response": "The response parameter is invalid or malformed.",
  "timeout-or-duplicate": "The response is no longer valid: either is too old or has been used previously."
} as const
Add reCaptcha Provider

Next, import the GoogleReCaptchaProvider from react-google-recaptcha-v3 and wrap your component to automatically load reCAPTCHA. The GoogleReCaptchaProvider ensures that reCAPTCHA is loaded for your application.

Here, I created new component and exported the Provider.

Note pass your "reCaptcha site Key" as prop reCaptchaKey to the GoogleReCaptchaProvider:

_providers/ReCaptcha.tsx
'use client'
import { GoogleReCaptchaProvider } from 'react-google-recaptcha-v3';
import React, { type PropsWithChildren } from 'react'
import env from '@/env'
 
 
export default function ReCaptchaProvider({ children }: Readonly<PropsWithChildren>) {
  return <GoogleReCaptchaProvider reCaptchaKey={env.recaptcha.siteKey}>{children}</GoogleReCaptchaProvider>
}

Server-Side Verification

After receiving the token from reCAPTCHA v3 on the client side, you must send it to your server for verification with Google's API.

As mentioned in the Document, we use URL: https://www.google.com/recaptcha/api/siteverify with POST method

Create a route.ts in the api folder, and using the verification link pass secret and token which is generated from client-end as searchParams.

v1/verify-recaptcha/route.ts
import env from "@/env"
import { NextResponse } from "next/server";
 
export async function POST(request: Request) {
  const body = await request.json()
 
  const { token } = body
  const secretKey = env.recaptcha.secret;
  const reCaptchaUrl = new URL('https://www.google.com/recaptcha/api/siteverify')
  reCaptchaUrl.searchParams.append('secret', secretKey)
  reCaptchaUrl.searchParams.append('response', token)
 
  const verificationResponce = await fetch(reCaptchaUrl, {
    method: 'POST'
  })
 
  const verification = await verificationResponce.json()
 
  if (verification.success && verification.score > 0.5) {
    return NextResponse.json({
      ...verification,
      success: true
    })
  } else {
    return NextResponse.json({
      ...verification,
      success: false
    })
  }
}

You may noticed I usally keep all the environment related keys in the env.ts file, so avoid miss spelling the environment keys.

How to use these components

Later you can import custom hook useReCaptcha use it as follow:

'use client'
import { type FormEvent } from 'react'
import { useReCaptcha } from '@/hooks/useReCaptcha'
 
export default function Form() {
  //  ... your code
 
  const { verifyCaptcha } = useReCaptcha()
 
  async function onSubmit(evt: FormEvent<HTMLFormElement>) {
    evt.preventDefault()
 
    const isReCapthaVerified = await verifyCaptcha()
 
    if (isReCapthaVerified ) {
      // perform form submission process here
    }
  }
 
  //  ... your code
 
  return (
      <form onSubmit={onSubmit}>
      // ...
      </form>
  )
}
 

I also created a layout file as in my scenario you can use Provider component where it is necessary:

import ReCaptchaProvider from "../_providers/ReCaptcha"
 
export default function PageLayout({
  children
}: {
  children: React.ReactNode
}) {
  return (
    <ReCaptchaProvider>
      {children}
    </ReCaptchaProvider>
  )
}

This type of approach is completly depends on developers and thier way coding styles.

Caution: This is how I implimented in my project there are other method to integrate in nextJs project either by using server actions or by injecting the script to the page

Conclusion

Google reCAPTCHA v3 offers a seamless, non-intrusive way to protect your Next.js application from bots. By using the react-google-recaptcha-v3 package, you can easily integrate this service into your forms. Make sure to test both client-side and server-side logic to ensure your implementation is secure and functional.