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 🚀.