Set up app open ads

App open ads appear when your users open or return to your app. Your users can close app open ads at any time. To monetize your app load screen, use app open ads. For details, see App open ad guidance and best practices.

This guide explains how to integrate app open ads into a React Native app.

Prerequisites

Before you continue, do the following:

Android

ca-app-pub-3940256099942544/9257395921

iOS

ca-app-pub-3940256099942544/5575463023

Understand ad preloading

Ad preloading in Google Mobile Ads SDK for React Native automates ad loading and caching.

Ad preloading provides the following benefits:

  • Reference management: maintains references until ads are shown.
  • Automatic reloading: loads a new ad when one is retrieved from the cache.
  • Managed retries: loads a new ad when one fails to load.
  • Expiration handling: refreshes ads before expiring.
  • Cache optimization: optimizes the cache order to deliver the highest priority ad.

To load ads manually, see the Load a single app open ad guide.

Start preloading

To start preloading ads, call the AppOpenAdPreloader.start method and pass your preloadId value, configuration settings, and event callbacks to monitor buffer state changes:

export function startAppOpenAdPreloader(
  adUnitId: string,
  preloadId: string,
  customPreloadCallback?: PreloadCallback,
): boolean {
  const started = AppOpenAdPreloader.start(
    preloadId,
    {
      request: {adUnitId},
      bufferSize: 2,
    },
    {
      // Called when an ad is preloaded into the buffer.
      onAdPreloaded: (event) => {
        console.log(`App open ad preloaded for ${event.preloadId}`);
        customPreloadCallback?.onAdPreloaded?.(event);
      },
      // Called when an ad fails to preload.
      onAdFailedToPreload: (error: LoadAdError) => {
        console.error('Failed to preload app open ad:', error.message);
        customPreloadCallback?.onAdFailedToPreload?.(error);
      },
      // Called when all preloaded ads in the buffer have been shown.
      onAdsExhausted: () => {
        console.log('App open ad buffer exhausted.');
        customPreloadCallback?.onAdsExhausted?.();
      },
    },
  );
  return started;
}

A preloadId is a string that uniquely identifies an ad preloading configuration. If the adUnitId is used for only a single preloading configuration in your app, you can use that ad unit ID as the preload ID. If your app requires multiple preloaders for the same ad unit ID, pass distinct strings for each preload ID.

When your app polls a preloaded ad from the buffer, Google Mobile Ads SDK for React Native requests a replacement ad in the background to refill the buffer. If no preloaded ads remain in the buffer, Google Mobile Ads SDK for React Native invokes the onAdsExhausted callback.

Preload cache limits

Google Mobile Ads SDK for React Native enforces an app-wide limit on the total number of preloaded ads:

  • Default limit: Google holds a maximum of six preloaded ads in memory.
  • We recommend keeping a buffer size of two for each preload ID.

If your app reaches the app-wide limit, background requests for preloaded ads pause until existing ads are polled and displayed or destroyed.

Poll and show the ad

To check for available preloaded ads and retrieve an ad instance, call the AppOpenAdPreloader.pollAd method.

To listen for full-screen presentation events, register callbacks on the adEventCallback property of the returned AppOpenAd instance before calling the show method:

export function showPreloadedAppOpenAd(
  preloadId: string,
  customAdEventCallback?: FullScreenAdEventCallback,
) {
  if (!AppOpenAdPreloader.isAdAvailable(preloadId)) {
    console.log('No preloaded app open ad available right now.');
    return;
  }

  const ad = AppOpenAdPreloader.pollAd(preloadId);
  if (!ad) {
    return;
  }

  ad.adEventCallback = {
    // Called when the ad is presented on screen.
    onAdShowedFullScreenContent: () => {
      console.log('Preloaded app open ad showed full screen content.');
      customAdEventCallback?.onAdShowedFullScreenContent?.();
    },
    // Called when the ad is closed by the user.
    onAdDismissedFullScreenContent: () => {
      console.log('Preloaded app open ad dismissed full screen content.');
      customAdEventCallback?.onAdDismissedFullScreenContent?.();
      ad.destroy();
    },
    // Called when presentation fails.
    onAdFailedToShowFullScreenContent: (error) => {
      console.error('Failed to show preloaded app open ad:', error);
      customAdEventCallback?.onAdFailedToShowFullScreenContent?.(error);
      ad.destroy();
    },
    // Called when an impression is recorded.
    onAdImpression: () => {
      console.log('Preloaded app open ad recorded an impression.');
      customAdEventCallback?.onAdImpression?.();
    },
    // Called when the user taps the ad.
    onAdClicked: () => {
      console.log('Preloaded app open ad was clicked.');
      customAdEventCallback?.onAdClicked?.();
    },
    // Called when the ad is estimated to have earned money.
    onAdPaid: (adValue) => {
      console.log(
        `Preloaded app open ad paid: ${adValue.amountMicros}` +
          ` ${adValue.currencyCode}`,
      );
      customAdEventCallback?.onAdPaid?.(adValue);
    },
  };

  ad.show();
}

To release memory and resources, call the ad.destroy method in the onAdDismissedFullScreenContent and onAdFailedToShowFullScreenContent callbacks when a user dismisses an ad or the ad fails to show.

Read the response info

To inspect details of the next preloaded ad without removing the ad from the cache, call the AppOpenAdPreloader.peekAdResponseInfo method:

export function peekAppOpenAdResponseInfo(
  preloadId: string,
): ResponseInfo | null {
  const responseInfo = AppOpenAdPreloader.peekAdResponseInfo(preloadId);
  if (responseInfo) {
    console.log('Next preloaded app open ad response info:', responseInfo);
  }
  return responseInfo;
}

Manage app open ads with an app-level manager

Because app open ads are presented globally during app launch and foreground transitions, we recommend managing preloader lifecycle state in an app-level manager outside the React component tree.

Using an app-level manager offers several key advantages:

  • Keeps preloader state independent of individual screen navigation and component lifecycle.
  • Centralizes ad event callbacks, state tracking, and cleanup in a single shared instance across your app.
  • Controls when foreground events are listened to, preventing duplicate presentations upon ad dismissal.

The following example implements an app-level manager using the AppOpenAdPreloader object:

export class AppOpenAdManager {
  private adUnitId: string;
  private preloadId: string;
  private appStateSubscription?: NativeEventSubscription;
  private startTimeoutId?: ReturnType<typeof setTimeout>;

  constructor(adUnitId: string, preloadId: string) {
    this.adUnitId = adUnitId;
    this.preloadId = preloadId;
  }

  start(): boolean {
    if (!AppOpenAdPreloader.getConfiguration(this.preloadId)) {
      AppOpenAdPreloader.start(
        this.preloadId,
        {
          request: {adUnitId: this.adUnitId},
          bufferSize: 2,
        },
        {
          onAdPreloaded: (event) => {
            console.log(`App open ad preloaded for ${event.preloadId}`);
          },
          onAdFailedToPreload: (error: LoadAdError) => {
            console.error('Failed to preload app open ad:', error.message);
          },
          onAdsExhausted: () => {
            console.log('App open ad buffer exhausted.');
          },
        },
      );
    }

    if (!this.appStateSubscription && !this.startTimeoutId) {
      // Delay attaching the listener to ensure native dismissal transitions complete
      this.startTimeoutId = setTimeout(() => {
        this.startTimeoutId = undefined;
        if (!this.appStateSubscription) {
          this.appStateSubscription = AppState.addEventListener(
            'change',
            (nextAppState: AppStateStatus) => {
              if (nextAppState === 'active') {
                this.showAdIfAvailable();
              }
            },
          );
        }
      }, APP_STATE_LISTENER_DELAY_MS);
    }

    return true;
  }

  pause(): void {
    if (this.startTimeoutId) {
      clearTimeout(this.startTimeoutId);
      this.startTimeoutId = undefined;
    }
    this.appStateSubscription?.remove();
    this.appStateSubscription = undefined;
  }

  stop(): void {
    this.pause();
    AppOpenAdPreloader.destroy(this.preloadId);
  }

  showAdIfAvailable() {
    if (!AppOpenAdPreloader.isAdAvailable(this.preloadId)) {
      console.log('App open ad is not preloaded or available right now.');
      return;
    }

    const ad = AppOpenAdPreloader.pollAd(this.preloadId);
    if (!ad) {
      return;
    }

    // Pause listener while presenting the ad
    this.pause();

    ad.adEventCallback = {
      onAdShowedFullScreenContent: () => {
        console.log('Preloaded app open ad showed full screen content.');
      },
      onAdDismissedFullScreenContent: () => {
        console.log('Preloaded app open ad dismissed full screen content.');
        ad.destroy();
        this.start();
      },
      onAdFailedToShowFullScreenContent: (error) => {
        console.error('Failed to show preloaded app open ad:', error);
        ad.destroy();
        this.start();
      },
    };

    ad.show();
  }
}

Listen to app foregrounding events

To display ads when your app returns to the foreground, register a change listener on the AppState module in your app's root component, such as App.tsx. Check when the state transitions to active. For example, call the start method on your ad manager instance in the App.tsx component.

Presenting ad overlays or landing pages, such as showing full-screen ads or tapping a banner ad, pushes your application to the background. When the user returns to your application, your app receives a new active state event. To prevent an unintended ad presentation or loop where returning from an ad presents an app open ad from the cache, do the following:

  1. Remove the AppState event listener before presenting a full-screen ad.
  2. To make sure listening resumes whether a user dismisses the ad or the ad fails to show, add the AppState event listener again in the onAdDismissedFullScreenContent and onAdFailedToShowFullScreenContent callbacks.

For a complete example of controlling when foreground event listeners are paused and resumed, see the AppOpenAdManager implementation.

Learn cold starts and loading screens

A cold start occurs when your app launches from scratch, for example, when a user opens your app for the first time or after the device closes the app to free memory. During a cold start, you lack a previously loaded app open ad to show next.

Because of ad request delays, your users might interact with your app before an ad appears. Show app open ads on cold starts only from a loading screen while app assets load. If assets finish loading and the user reaches the main content before the ad loads, don't show the ad.

Clean up resources

When preloading is no longer required for a screen or ad unit ID, such as when navigating away from a screen that preloads ads, call the AppOpenAdPreloader.destroy method to release preloader resources and prevent holding unused ad objects in memory:

export function destroyAppOpenAdPreloader(preloadId: string) {
  AppOpenAdPreloader.destroy(preloadId);
}

In addition to destroying the preloader queue, make sure to destroy individual polled ad instances when a user dismisses an ad or the ad fails to show. For details, see Poll and show the ad.

View the full example

The following example component starts preloading on mount, tracks available ad counts, and provides controls to show preloaded app open ads:

export function AppOpenScreen() {
  const [numAdsAvailable, setNumAdsAvailable] = useState<number>(0);
  const [status, setStatus] = useState<string>('Preloader idle.');

  const refreshAdCount = () => {
    const count = AppOpenAdPreloader.getNumAdsAvailable(PRELOAD_ID);
    setNumAdsAvailable(count);
  };

  useEffect(() => {
    // Start preloading ads on component mount
    const started = startAppOpenAdPreloader(APP_OPEN_AD_UNIT_ID, PRELOAD_ID, {
      onAdPreloaded: () => {
        refreshAdCount();
        setStatus('App open ad preloaded into buffer.');
      },
      onAdFailedToPreload: (error) => {
        setStatus(`Failed to preload app open ad: ${error.message}`);
      },
      onAdsExhausted: () => {
        refreshAdCount();
        setStatus('Preload buffer exhausted for app open ads.');
      },
    });
    if (started) {
      setStatus('Preloader started.');
    }

    // Clean up preloader resources on component unmount
    return () => {
      destroyAppOpenAdPreloader(PRELOAD_ID);
    };
  }, []);

  const handleShowAd = () => {
    setStatus('Showing preloaded ad...');
    showPreloadedAppOpenAd(PRELOAD_ID, {
      onAdDismissedFullScreenContent: () => {
        setStatus('Ad dismissed.');
      },
      onAdFailedToShowFullScreenContent: (error) => {
        setStatus(`Failed to show preloaded ad: ${error.message}`);
      },
    });
    refreshAdCount();
  };

  return (
    <View style={styles.container}>
      <Text style={styles.title}>Preload App Open Ads</Text>
      <Text style={styles.status}>Status: {status}</Text>
      <Text style={styles.status}>Ads Available: {numAdsAvailable}</Text>
      <View style={styles.buttonGroup}>
        <Button
          title="Show Preloaded Ad"
          onPress={handleShowAd}
          disabled={numAdsAvailable === 0}
        />
      </View>
    </View>
  );
}

Replace the following:

  • APP_OPEN_AD_UNIT_ID with your ad unit ID.
  • PRELOAD_ID with your preloader ID.

Next steps

After you integrate app open ads, explore the following topics: