Feb 05 20233 min read
Lazy ReCAPTCHA

Blogs

Lazy ReCAPTCHA

When adding ReCAPTCHA to landing pages, it can result in slower page load times 🐢. To avoid this, it's important to implement lazy loading for ReCAPTCHA. This means that the ReCAPTCHA component will only be loaded when it's necessary 🙌.


Here I'll show you how to lazy load ReCAPTCHA in React using NextJS and the react-google-recaptcha library.


The solution employs dynamic imports in NextJS to defer loading of the ReCAPTCHA component and provides access to its API.


You can see a working example in a codesandbox by clicking here 🔗 and the code on GitHub here 👨‍💻

1️⃣ We'll create a component that will be lazy loaded

// components/recaptcha-ref-forward.tsx
import { LegacyRef } from "react";
import ReCAPTCHA, { ReCAPTCHAProps } from "react-google-recaptcha";

type Props = ReCAPTCHAProps & {
  forwardedRef: LegacyRef<ReCAPTCHA> | undefined;
};

const RecaptchaRefForward = ({ forwardedRef, ...props }: Props) => (
  <ReCAPTCHA {...props} ref={forwardedRef} />
);

export default RecaptchaRefForward;

We are going to forward the ref so the parent component can use it to expose ReCAPTCHA's APIs.

2️⃣ We'll create a component that lazy loads the ReCAPTCHA and exposes its API.

// components/recaptcha.tsx
import dynamic from "next/dynamic";
import React, { Dispatch, SetStateAction } from "react";
import ReCAPTCHA, { ReCAPTCHAProps } from "react-google-recaptcha";

const RecaptchaWithRefForward = dynamic(
  import("@/components/recaptcha-ref-forward")
);

const ReCAPTCHAComp = React.forwardRef(
  (props: ReCAPTCHAProps, ref: React.LegacyRef<ReCAPTCHA> | undefined) => (
    <RecaptchaWithRefForward {...props} forwardedRef={ref} />
  )
);

ReCAPTCHAComp.displayName = "ReCAPTCHA";

type Props = {
  shouldRecaptchaLoad: boolean;
  onChange: ((token: string | null) => void) | undefined;
  setRecaptchaAPI: Dispatch<SetStateAction<ReCAPTCHA | null>>;
};

const TEST_RECAPTCHA_SITE_KEY = "6LeIxAcTAAAAAJcZVRqyHh71UMIEGNQ_MXjiZKhI";

const LazyRecaptcha = ({
  shouldRecaptchaLoad,
  onChange,
  setRecaptchaAPI,
}: Props) => {
  if (!shouldRecaptchaLoad) return null;
  return (
    <ReCAPTCHAComp
      size="invisible"
      sitekey={
        process.env.NEXT_PUBLIC_RECAPTCHA_SITE_KEY || TEST_RECAPTCHA_SITE_KEY
      }
      onChange={onChange}
      ref={(ref) => setRecaptchaAPI(ref)}
    />
  );
};

export default React.memo(LazyRecaptcha);

This component is loaded using next/dynamic and ref forwarding is done here because it is not supported for dynamically imported components.

3️⃣ Next, we'll create a custom hook that can be used to access the ReCAPTCHA API.

// hooks/useLazyRecaptcha.ts
import { useState } from "react";
import ReCAPTCHA from "react-google-recaptcha";

const useLazyRecaptcha = () => {
  const [shouldShowRecaptcha, setShouldShowRecaptcha] = useState(false);
  const [recaptchaApi, setRecaptchaApi] = useState<ReCAPTCHA | null>(null);

  return {
    shouldShowRecaptcha,
    setShouldShowRecaptcha,
    recaptchaApi,
    setRecaptchaApi,
  };
};

export default useLazyRecaptcha;

This hook is used along with the lazy loading component to use the ReCAPTCHA API.

4️⃣ Here's how you can use the lazy loading ReCAPTCHA in your code:

// pages/index.tsx
import LazyRecaptcha from "@/components/recaptcha";
import useLazyRecaptcha from "@/hooks/useLazyRecaptcha";

export default function Home() {
  const {
    shouldShowRecaptcha,
    recaptchaApi,
    setRecaptchaApi,
    setShouldShowRecaptcha,
  } = useLazyRecaptcha();

  const doPostRecaptchaTasks = () => {
    console.log("Verified!");
  };

  return (
    <div style={{ display: "flex", gap: "20px", padding: "20px" }}>
      {/* Can be any user interaction */}
      <button onClick={() => setShouldShowRecaptcha(true)}>
        Load ReCAPTCHA
      </button>
      {shouldShowRecaptcha && (
        <button onClick={() => recaptchaApi?.execute()}>
          Run ReCAPTCHA verification
        </button>
      )}
      <LazyRecaptcha
        shouldRecaptchaLoad={shouldShowRecaptcha}
        onChange={doPostRecaptchaTasks}
        setRecaptchaAPI={setRecaptchaApi}
      />
    </div>
  );
}

Lazy loading ReCAPTCHA is a simple solution to improve the performance of your landing pages ⚡️. It reduces the initial page load time and ensures that ReCAPTCHA only loads when it's necessary, which improves the user experience 🚀.