Skip to main content

Overview

withGoogleReCaptcha is a Higher-Order Component (HOC) that injects reCAPTCHA functionality into your components via props. It’s primarily useful for class components or when you prefer HOC patterns over hooks. Source: src/with-google-recaptcha.tsx:14

Import

import { withGoogleReCaptcha } from 'react-google-recaptcha-v3';

Signature

const withGoogleReCaptcha = function <OwnProps>(
  Component: ComponentType<OwnProps & Partial<IWithGoogleReCaptchaProps>>
): ComponentType<OwnProps & Partial<IWithGoogleReCaptchaProps>>

Parameters

Component
ComponentType<OwnProps & Partial<IWithGoogleReCaptchaProps>>
required
The component to wrap. It will receive a googleReCaptchaProps prop containing reCAPTCHA functionality.

Injected Props

The wrapped component receives the following prop:
googleReCaptchaProps
IGoogleReCaptchaConsumerProps
Object containing reCAPTCHA functionality:

Return Value

Returns a wrapped component with the same props as the original component, plus googleReCaptchaProps.

Features

  • Preserves Component Name: The wrapped component’s displayName is set to withGoogleReCaptcha(ComponentName)
  • Hoists Static Members: Non-React static properties from the original component are preserved using hoist-non-react-statics

Usage Examples

Basic Class Component

import React, { Component } from 'react';
import { withGoogleReCaptcha, IWithGoogleReCaptchaProps } from 'react-google-recaptcha-v3';

interface Props extends IWithGoogleReCaptchaProps {
  // Your component props
  username: string;
}

class LoginForm extends Component<Props> {
  handleSubmit = async () => {
    const { googleReCaptchaProps } = this.props;
    const { executeRecaptcha } = googleReCaptchaProps;

    if (!executeRecaptcha) {
      console.log('Recaptcha not ready');
      return;
    }

    const token = await executeRecaptcha('login');
    console.log('Token:', token);
    // Submit to backend
  };

  render() {
    return (
      <form>
        <input type="text" value={this.props.username} />
        <button onClick={this.handleSubmit}>Login</button>
      </form>
    );
  }
}

export default withGoogleReCaptcha(LoginForm);

Functional Component (Alternative to Hook)

import { withGoogleReCaptcha, IWithGoogleReCaptchaProps } from 'react-google-recaptcha-v3';

interface Props extends IWithGoogleReCaptchaProps {
  onSubmit: (data: any) => void;
}

function ContactForm({ googleReCaptchaProps, onSubmit }: Props) {
  const handleSubmit = async () => {
    const { executeRecaptcha } = googleReCaptchaProps;

    if (!executeRecaptcha) {
      console.log('Recaptcha not ready');
      return;
    }

    const token = await executeRecaptcha('contact');
    onSubmit({ token });
  };

  return (
    <form>
      <button onClick={handleSubmit}>Submit</button>
    </form>
  );
}

export default withGoogleReCaptcha(ContactForm);

With Multiple Actions

import React, { Component } from 'react';
import { withGoogleReCaptcha, IWithGoogleReCaptchaProps } from 'react-google-recaptcha-v3';

class Dashboard extends Component<IWithGoogleReCaptchaProps> {
  executeAction = async (action: string) => {
    const { executeRecaptcha } = this.props.googleReCaptchaProps;

    if (!executeRecaptcha) {
      throw new Error('Recaptcha not ready');
    }

    return await executeRecaptcha(action);
  };

  handleLogin = async () => {
    const token = await this.executeAction('login');
    // Process login
  };

  handleLogout = async () => {
    const token = await this.executeAction('logout');
    // Process logout
  };

  handleDelete = async () => {
    const token = await this.executeAction('delete');
    // Process deletion
  };

  render() {
    return (
      <div>
        <button onClick={this.handleLogin}>Login</button>
        <button onClick={this.handleLogout}>Logout</button>
        <button onClick={this.handleDelete}>Delete Account</button>
      </div>
    );
  }
}

export default withGoogleReCaptcha(Dashboard);

With State and Error Handling

import React, { Component } from 'react';
import { withGoogleReCaptcha, IWithGoogleReCaptchaProps } from 'react-google-recaptcha-v3';

interface State {
  loading: boolean;
  error: string | null;
}

class SecureForm extends Component<IWithGoogleReCaptchaProps, State> {
  state: State = {
    loading: false,
    error: null
  };

  handleSubmit = async (e: React.FormEvent) => {
    e.preventDefault();
    const { executeRecaptcha } = this.props.googleReCaptchaProps;

    if (!executeRecaptcha) {
      this.setState({ error: 'reCAPTCHA not ready' });
      return;
    }

    this.setState({ loading: true, error: null });

    try {
      const token = await executeRecaptcha('form_submit');
      
      const response = await fetch('/api/submit', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ token })
      });

      if (!response.ok) {
        throw new Error('Submission failed');
      }

      console.log('Success!');
    } catch (error) {
      this.setState({
        error: error instanceof Error ? error.message : 'Unknown error'
      });
    } finally {
      this.setState({ loading: false });
    }
  };

  render() {
    const { loading, error } = this.state;
    const { executeRecaptcha } = this.props.googleReCaptchaProps;

    return (
      <form onSubmit={this.handleSubmit}>
        {error && <div className="error">{error}</div>}
        <button type="submit" disabled={loading || !executeRecaptcha}>
          {loading ? 'Submitting...' : 'Submit'}
        </button>
      </form>
    );
  }
}

export default withGoogleReCaptcha(SecureForm);

Composing with Other HOCs

import React, { Component } from 'react';
import { withGoogleReCaptcha, IWithGoogleReCaptchaProps } from 'react-google-recaptcha-v3';
import { connect } from 'react-redux';
import { compose } from 'redux';

interface OwnProps {
  userId: string;
}

interface StateProps {
  user: any;
}

interface DispatchProps {
  updateUser: (data: any) => void;
}

type Props = OwnProps & StateProps & DispatchProps & IWithGoogleReCaptchaProps;

class UserProfile extends Component<Props> {
  handleUpdate = async () => {
    const { executeRecaptcha } = this.props.googleReCaptchaProps;

    if (!executeRecaptcha) return;

    const token = await executeRecaptcha('update_profile');
    this.props.updateUser({ token, userId: this.props.userId });
  };

  render() {
    return (
      <div>
        <h1>{this.props.user.name}</h1>
        <button onClick={this.handleUpdate}>Update Profile</button>
      </div>
    );
  }
}

const mapStateToProps = (state: any): StateProps => ({
  user: state.user
});

const mapDispatchToProps: DispatchProps = {
  updateUser: (data) => ({ type: 'UPDATE_USER', payload: data })
};

export default compose(
  connect(mapStateToProps, mapDispatchToProps),
  withGoogleReCaptcha
)(UserProfile);

TypeScript with Strict Typing

import React, { Component } from 'react';
import {
  withGoogleReCaptcha,
  IWithGoogleReCaptchaProps,
  IGoogleReCaptchaConsumerProps
} from 'react-google-recaptcha-v3';

interface OwnProps {
  title: string;
  onSuccess: (token: string) => void;
}

type Props = OwnProps & IWithGoogleReCaptchaProps;

class ProtectedAction extends Component<Props> {
  private get recaptcha(): IGoogleReCaptchaConsumerProps {
    return this.props.googleReCaptchaProps;
  }

  private async getToken(action: string): Promise<string | null> {
    const { executeRecaptcha } = this.recaptcha;

    if (!executeRecaptcha) {
      console.warn('reCAPTCHA not available');
      return null;
    }

    try {
      return await executeRecaptcha(action);
    } catch (error) {
      console.error('reCAPTCHA error:', error);
      return null;
    }
  }

  handleAction = async () => {
    const token = await this.getToken('protected_action');
    
    if (token) {
      this.props.onSuccess(token);
    }
  };

  render() {
    const isReady = !!this.recaptcha.executeRecaptcha;

    return (
      <div>
        <h2>{this.props.title}</h2>
        <button onClick={this.handleAction} disabled={!isReady}>
          {isReady ? 'Execute Action' : 'Loading...'}
        </button>
      </div>
    );
  }
}

export default withGoogleReCaptcha(ProtectedAction);

Display Name

The HOC automatically sets a descriptive display name for debugging:
class MyComponent extends Component {}

const Wrapped = withGoogleReCaptcha(MyComponent);
console.log(Wrapped.displayName); // "withGoogleReCaptcha(MyComponent)"

Static Properties

The HOC preserves static properties using hoist-non-react-statics:
class MyComponent extends Component {
  static myStaticMethod() {
    return 'hello';
  }
}

const Wrapped = withGoogleReCaptcha(MyComponent);
console.log(Wrapped.myStaticMethod()); // "hello"

TypeScript Interfaces

interface IWithGoogleReCaptchaProps {
  googleReCaptchaProps: IGoogleReCaptchaConsumerProps;
}

interface IGoogleReCaptchaConsumerProps {
  executeRecaptcha?: (action?: string) => Promise<string>;
  container?: string | HTMLElement;
}
See TypeScript Interfaces for complete type definitions.

Comparison with useGoogleReCaptcha Hook

FeaturewithGoogleReCaptcha HOCuseGoogleReCaptcha Hook
Component typeClass & FunctionFunction only
Access patternPropsHook return value
ComposabilityCan chain with other HOCsCompose with other hooks
ReadabilityMore boilerplateMore concise
Modern ReactLegacy patternModern pattern
Use the HOC when:
  • Working with class components
  • Composing multiple HOCs together
  • Maintaining legacy codebases
  • You prefer HOC patterns
Use the hook when:
  • Working with functional components
  • Following modern React patterns
  • Want cleaner, more readable code
  • Building new features

Important Notes

Provider Required

Components wrapped with withGoogleReCaptcha must be descendants of GoogleReCaptchaProvider:
import { GoogleReCaptchaProvider } from 'react-google-recaptcha-v3';
import MyWrappedComponent from './MyWrappedComponent';

function App() {
  return (
    <GoogleReCaptchaProvider reCaptchaKey="YOUR_SITE_KEY">
      <MyWrappedComponent />
    </GoogleReCaptchaProvider>
  );
}

Check Availability

Always check if executeRecaptcha is available before calling:
const { executeRecaptcha } = this.props.googleReCaptchaProps;

if (!executeRecaptcha) {
  // Not ready yet
  return;
}

Build docs developers (and LLMs) love