---
title: Native ads
description: Render native ads with your own layout using NativeAd, NativeAdView, and NativeAsset.
---

Native ads are ad assets that are presented to users through UI components that are native to the platform.
They're shown using the same types of views with which you're already building your layouts, and can be formatted to match your app's visual design.

When a native ad loads, your app receives an ad object that contains its assets, and the app — rather than the Google Mobile Ads SDK — is responsible for displaying them.

Broadly speaking, there are two parts to successfully implementing native ads: Loading an ad using the SDK and then displaying the ad content in your app.

<img
  width="300"
  src="https://developers.google.com/static/admob/images/format-native.svg"
  alt="Native ad assets arranged in a custom app layout"
/>

<Warning>
  Complete consent and SDK initialization before calling `NativeAd.createForAdRequest()` or letting
  `useNativeAd` load (`autoLoad: consentReady`). See [Consent & privacy basics](/consent-basics) and
  the shared [load best practices](/ad-formats#load-and-refresh-best-practices).
</Warning>

For a **one-request native-or-banner** competitive load (GAM, count 1), or an emulated
display pool that keeps one ready fill, see
[Preload pools and multi-format recipes](/preload-pools-and-multiformat-recipes).

## Load ads

In a React component, load with the `useNativeAd` hook. It owns the `NativeAd`: it destroys the ad
when the component unmounts or when `adUnitId` / `requestOptions` change, including a load that
resolves after unmount. For testing, use a Test ID; in production, use the ID from the Google AdMob
dashboard under "Ad units":

```tsx
import { TestIds, useNativeAd } from 'react-native-google-mobile-ads';

const { status, nativeAd, error, retry, destroy } = useNativeAd({
  adUnitId: TestIds.NATIVE,
  autoLoad: consentReady,
});
```

- `status` is `'idle'`, `'loading'`, `'loaded'`, `'no-fill'`, or `'error'`. `nativeAd` is non-null
  only when `status` is `'loaded'`; `error` is set for `'no-fill'` and `'error'`.
- `autoLoad` defaults to `true`. Set it to `false` until consent is resolved. Turning it back off
  stops future automatic loads only; it does not destroy a loaded ad.
- `retry()` loads again (after `'no-fill'` / `'error'`, or the first load when `autoLoad` is
  false). Automatic loading never retries on its own.
- `destroy()` destroys the owned ad and returns to `'idle'` without loading again. Do not call
  `nativeAd.destroy()` yourself on an ad the hook owns.

Outside React, call the `NativeAd.createForAdRequest` static method directly. It returns a promise
that resolves with a `NativeAd` object, and you own `destroy()`:

```tsx
const nativeAd = await NativeAd.createForAdRequest(TestIds.NATIVE);
```

### Native ad options

Native ads have many advanced features that allow you to make additional customizations to create the best possible ad experience.

Pass an optional `NativeAdRequestOptions` object as `requestOptions` to `useNativeAd`, or as the
second argument to `NativeAd.createForAdRequest`, to customize the ad request:

```tsx
NativeAd.createForAdRequest(TestIds.NATIVE, {
  aspectRatio: NativeMediaAspectRatio.LANDSCAPE,
});
```

#### Preferred media aspect ratio controls

Media Aspect Ratio Controls let you specify a preference for the aspect ratio of ad creatives.

<Info>
  Use of this API does not guarantee that all ad creatives will meet the preference specified.
</Info>

Specify `requestOptions.aspectRatio` with a `NativeMediaAspectRatio` enum value when calling `NativeAd.createForAdRequest`.

- When unset, the returned ad can have any media aspect ratio.
- When set, you will be able to improve the user experience by specifying the preferred type of aspect ratio.

The following example instructs the SDK to prefer a return image or video with a specific aspect ratio.

```tsx
NativeAd.createForAdRequest(TestIds.NATIVE, {
  aspectRatio: NativeMediaAspectRatio.LANDSCAPE,
});
```

#### AdChoices placements

The AdChoices position controls lets you choose which corner to render the AdChoices icon.

Specify `requestOptions.adChoicesPlacement` with a `NativeAdChoicesPlacement` enum value when calling `NativeAd.createForAdRequest`.

- If unset, the AdChoices icon position is set to the top right.
- If set, AdChoices is placed at the custom position as requested.

The following example demonstrates how to set a custom AdChoices image position.

```tsx
NativeAd.createForAdRequest(TestIds.NATIVE, {
  adChoicesPlacement: NativeAdChoicesPlacement.TOP_LEFT,
});
```

#### Start mute behavior

The start muted behavior lets you disable or enable a video's starting audio.

Specify `requestOptions.startVideoMuted` with a boolean value when calling `NativeAd.createForAdRequest`.

- The start muted behavior is enabled by default.
- When disabled, your app requests the video should begin with audio.
- When enabled, your app requests that the video should begin with audio muted.

The following example shows how to start the video with unmuted audio.

```tsx
NativeAd.createForAdRequest(TestIds.NATIVE, {
  startVideoMuted: false,
});
```

#### Other ad request options

You can specify other request options to be sent while loading an advert, such as keywords and
[content URLs](/ad-formats/interstitial#content-url-targeting-brand-safety).
Setting additional request options helps AdMob choose better tailored ads from the network.
View the [RequestOptions](https://github.com/invertase/react-native-google-mobile-ads/blob/main/packages/core/src/types/RequestOptions.ts) source code to see the full range of options available.

## Display ads

When a native ad loads, your app is then responsible for displaying the ad (though it doesn't necessarily have to do so immediately).
To make displaying system-defined ad formats easier, the SDK offers some useful resources, as described below.

### NativeAdView component

For the NativeAd format, there is the corresponding `NativeAdView` component.
This component is a wrapper component that publishers should use as the root for the NativeAd.
A single `NativeAdView` corresponds to a single native ad.
Each view used to display that ads assets should be placed within the view hierarchy of the `NativeAdView` component.
Pass the `NativeAd` object to the `nativeAd` prop of the `NativeAdView` component to register the ad view:

```tsx
import { NativeAdView, TestIds, useNativeAd } from 'react-native-google-mobile-ads';

const NativeComponent = ({ consentReady }: { consentReady: boolean }) => {
  const { nativeAd } = useNativeAd({ adUnitId: TestIds.NATIVE, autoLoad: consentReady });

  if (!nativeAd) {
    return null;
  }

  return (
    <NativeAdView nativeAd={nativeAd}>
      {/* Components to display assets must be placed here */}
    </NativeAdView>
  );
};
```

### NativeAsset component

`NativeAsset` component, which should be placed within the view hierarchy of a `NativeAdView`, is used to register the view used for each individual asset.
Registering the views in this way allows the SDK to automatically handle tasks such as:

- Recording clicks.
- Recording impressions (when the first pixel is visible on the screen).
- Displaying the AdChoices overlay.

`NativeAsset` component is a headless component, which means the component does not have UI implementation.
Customize your UI components as needed and place them inside the `NativeAsset` component, setting the `assetType` prop to the asset type you are displaying:

```tsx
const NativeComponent = ({ consentReady }: { consentReady: boolean }) => {
  const { nativeAd } = useNativeAd({ adUnitId: TestIds.NATIVE, autoLoad: consentReady });

  if (!nativeAd) {
    return null;
  }

  return (
    <NativeAdView nativeAd={nativeAd}>
      <NativeAsset assetType={NativeAssetType.HEADLINE}>
        <Text>{nativeAd.headline}</Text>
      </NativeAsset>
    </NativeAdView>
  );
};
```

<Warning>
  Place the asset view(such as `Text`, `Image` component) as a direct child of the `NativeAsset`
  component. See more in [Caveats: `NativeAsset` placement](#nativeasset-placement) section.
</Warning>

### NativeMediaView component

Main image and video assets are displayed to users via `NativeMediaView`. It should be placed within the view hierarchy of a `NativeAdView`, as with any other asset view.

Unlike other asset views, the `NativeMediaView` automatically populates its content.

```tsx
const NativeComponent = () => {
  return (
    <NativeAdView nativeAd={nativeAd}>
      <NativeMediaView />
      {/* Other assets */}
    </NativeAdView>
  );
};
```

#### Resize mode

The `NativeMediaView` component respects the `resizeMode` property when displaying images. The `resizeMode` defaults to `cover`.

Supports the following values:

- `cover`: Scale the image uniformly (maintain the image's aspect ratio) so that:
  - Both dimensions (width and height) of the image will be equal to or larger than the corresponding dimension of the view (minus padding)
  - At least one dimension of the scaled image will be equal to the corresponding dimension of the view (minus padding)
- `contain`: Scale the image uniformly (maintain the image's aspect ratio) so that both dimensions (width and height) of the image will be equal to or less than the corresponding dimension of the view (minus padding).
- `stretch`: Scale width and height independently, This may change the aspect ratio of the source image.

<Info>
  NativeMediaView's `aspectRatio` style property is set to `nativeAd.mediaContent.aspectRatio` by
  default.
</Info>

```tsx
const NativeComponent = () => {
  return (
    <NativeAdView nativeAd={nativeAd}>
      <NativeMediaView resizeMode={'contain'} style={{ aspectRatio: 1 }} />
      {/* Other assets */}
    </NativeAdView>
  );
};
```

### AdChoices overlay

An AdChoices overlay is added as an ad view by the SDK when a backfill ad is returned.
If your app uses native ads backfill, leave space in your
[preferred corner](#adchoices-placements) of your native ad view for the automatically inserted
AdChoices logo.
Also, it's important that the AdChoices overlay is seen, so choose background colors and images appropriately.
For more information on the overlay's appearance and function, refer to the [programmatic native ads implementation guidelines](https://support.google.com/admob/answer/6329638#adchoices).

### Ad attribution for programmatic native ads

When displaying programmatic native ads, you must display an ad attribution to denote that the view is an advertisement.
Learn more in [policy guidelines](https://support.google.com/admob/answer/6329638#ad-attribution).

### Code example

These are the steps for displaying a native ad:

1. Place the `NativeAdView`, register it by setting the `nativeAd` prop to the NativeAd object.
2. For each ad asset to be displayed:
   - Populate the asset view with the asset in the `NativeAd` object.
   - Register the asset view by wrapping it with the `NativeAsset` component.
3. Place the `NativeMediaView` inside your native ad layout to display media asset.

```tsx
const NativeComponent = ({ consentReady }: { consentReady: boolean }) => {
  const { status, nativeAd, retry } = useNativeAd({
    adUnitId: TestIds.NATIVE,
    autoLoad: consentReady,
  });

  if (status === 'no-fill' || status === 'error') {
    return <Button title="Retry" onPress={retry} />;
  }
  if (!nativeAd) {
    return null;
  }

  return (
    <NativeAdView nativeAd={nativeAd}>
      {/* Register every displayed asset inside NativeAdView. */}
      {nativeAd.icon && (
        <NativeAsset assetType={NativeAssetType.ICON}>
          <Image source={{ uri: nativeAd.icon.url }} width={24} height={24} />
        </NativeAsset>
      )}
      <NativeAsset assetType={NativeAssetType.HEADLINE}>
        <Text style={{ fontSize: 18, fontWeight: 'bold' }}>{nativeAd.headline}</Text>
      </NativeAsset>
      <NativeAsset assetType={NativeAssetType.CALL_TO_ACTION}>
        <Text style={{ padding: 8 }}>{nativeAd.callToAction}</Text>
      </NativeAsset>
      {/* Always display an ad attribution. */}
      <Text>Sponsored</Text>
      <NativeMediaView />
      {/* Repeat the registration pattern for the other assets. */}
    </NativeAdView>
  );
};
```

## Receiving ad events

The `NativeAd` object emits events that you can listen to.

Use the `addAdEventListener` method to listen to ad events emitted by the `NativeAd`. Pass one of the `NativeAdEventType` enum values to specify the event type.

Check the [NativeAdEventType](https://github.com/invertase/react-native-google-mobile-ads/blob/main/packages/core/src/NativeAdEventType.ts) source code to see the full range of events available.
Forward `NativeAdEventType.PAID` payloads to your
[revenue telemetry](/revenue-telemetry-and-auction-diagnostics) pipeline.

```tsx
import { useEffect } from 'react';
import {
  NativeAdEventType,
  NativeAdView,
  TestIds,
  useNativeAd,
} from 'react-native-google-mobile-ads';

const NativeComponent = ({ consentReady }: { consentReady: boolean }) => {
  const { nativeAd } = useNativeAd({ adUnitId: TestIds.NATIVE, autoLoad: consentReady });

  useEffect(() => {
    if (!nativeAd) return;
    const clickedListener = nativeAd.addAdEventListener(NativeAdEventType.CLICKED, () => {
      console.log('Native ad clicked');
    });
    const paidListener = nativeAd.addAdEventListener(NativeAdEventType.PAID, event => {
      console.log('Native ad revenue', event.value, event.currency);
    });
    return () => {
      clickedListener.remove();
      paidListener.remove();
    };
  }, [nativeAd]);

  if (!nativeAd) {
    return null;
  }

  return (
    <NativeAdView nativeAd={nativeAd}>
      {/* Components to display assets must be placed here */}
    </NativeAdView>
  );
};
```

## Destroying ads

Always destroy the `NativeAd` object when it is no longer needed to free up resources.
Calling `destroy` also removes all event listeners registered on the `NativeAd` object.

`useNativeAd` does this for you on unmount and when the request changes; call its `destroy()` to
drop the ad early. If you created the ad yourself with `NativeAd.createForAdRequest`, you own it:

```tsx
const nativeAd = await NativeAd.createForAdRequest(TestIds.NATIVE);
// ...render it in <NativeAdView nativeAd={nativeAd}>...
nativeAd.destroy();
```

## Caveats / limitations

### `NativeAsset` placement

Place the asset view(such as `Text`, `Image` component) as a direct child of the `NativeAsset` component,
and do not wrap the asset view with another view.
The SDK automatically handles tasks such as recording / handling clicks on the asset view.
Wrapping the asset view with another view can interfere with these tasks.

Do:

```tsx
<NativeAsset assetType={NativeAssetType.CALL_TO_ACTION}>
  <Text style={{ padding: 8 }}>{nativeAd.callToAction}</Text>
</NativeAsset>
<NativeAsset assetType={NativeAssetType.ICON}>
  <Image source={{ uri: nativeAd.icon.url }} width={24} height={24} style={{ borderRadius: 8 }} />
</NativeAsset>
```

Don't:

```tsx
<NativeAsset assetType={NativeAssetType.CALL_TO_ACTION}>
  <TouchableOpacity style={{ padding: 8 }}>
    <Text>{nativeAd.callToAction}</Text>
  </TouchableOpacity>
</NativeAsset>
<NativeAsset assetType={NativeAssetType.ICON}>
  <View style={{ borderRadius: 8, overflow: 'hidden' }}>
    <Image source={{ uri: nativeAd.icon.url }} width={24} height={24} />
  </View>
</NativeAsset>
```

<Info>
  Use `NativeAssetType.CALL_TO_ACTION` (wire value `'callToAction'`) and `nativeAd.callToAction`.
  There is no `NativeAssetType.CTA` or `nativeAd.cta` — those names never register the view with the
  SDK, so the button appears but does not open the click URL.
</Info>

### FlatList and preloaded native ads

You can preload several `NativeAd` instances and render them from a `FlatList` (or similar recycled list). When a cell reuses the same `NativeAdView` tree for a different ad, `NativeAsset` re-registers its views whenever `nativeAd.responseId` changes so SDK click handling stays attached. Prefer a stable `key` per ad (for example `nativeAd.responseId`) when the row should remount instead of rebind.

### Registered assets only

<Warning>
  Only views wrapped in `NativeAsset` (or `NativeMediaView`) are registered as GMA clickable assets.
  Do not wrap the entire `NativeAdView` layout in a single `NativeAsset`, and do not stretch one
  asset view across empty padding — that invents clickable white space and can conflict with AdMob
  policy. Decorative spacers and non-asset chrome should stay outside `NativeAsset`.
</Warning>

### Fullscreen pagers and native video

On Android, `NativeMediaView` needs a non-zero laid-out size while the page is visible before GMA can paint video. Prefer showing media from `nativeAd.mediaContent` (and give the view width + `aspectRatio` or an explicit height) rather than gating only on `nativeAd.images`:

{/* prettier-ignore */}
```tsx
{(nativeAd.mediaContent?.hasVideoContent || (nativeAd.images?.length ?? 0) > 0) && (
  <NativeMediaView style={{ width: '100%' }} />
)}
```

If a pager cell still briefly stays black, ensure the media view is mounted inside `NativeAdView` for that page (not swapped in only after images load) and that the cell is not `display: 'none'` / zero-height when it becomes the active page.

### Android 32-bit (`armeabi-v7a`) TurboModule EventEmitter crash

Play Console stacks that show
`JavaTurboModule::setEventEmitterCallback` →
`NativeGoogleMobileAdsNativeModuleSpecJSI` →
`RNGoogleMobileAdsSpec_ModuleProvider` on **`armeabi-v7a`** (often signed release / Play
splits) are a **React Native host bug**, not a defect in this library’s native module
implementation.

Native ads declare a Codegen `EventEmitter` (`onAdEvent`). During TurboModule
construction, React Native’s C++ layer calls Java `setEventEmitterCallback`. On 32-bit
ARM, the pre-fix path used variadic JNI `CallVoidMethod`, which is unsafe for that ABI
and can SIGSEGV in `art::ArgArray::BuildArgArrayFromVarArgs`.
The fix replaces that call with `CallVoidMethodA`
([PR #51695](https://github.com/facebook/react-native/pull/51695), backported to the
**0.79.6** line and present in later releases including this package’s peer baseline
**React Native ≥ 0.86.0**).

This package cannot patch `libreactnative.so` inside the app. If you still see the
stack:

1. Confirm the crash ABI is `armeabi-v7a` (or another 32-bit split) and the frames above.
2. Upgrade the **host** React Native to **≥ 0.79.6** (Expo: a SDK build whose RN patch
   includes that backport), or to this package’s peer **≥ 0.86.0**.
3. Rebuild and ship a new binary; Play Console stacks from older RN builds will keep
   appearing until those installs update.
4. If the same stack persists on a patched RN version, escalate to React Native with the
   full tombstone and your exact `react-native` version — do not expect a library-only
   workaround that disables the native-ad EventEmitter.

Reports that describe the same frames as Android ≤10-only (or as a missing Android
EventEmitter stub) are the same host bug — older OS versions correlate with 32-bit
devices, not an API-level or library stub gap
([#757](https://github.com/invertase/react-native-google-mobile-ads/issues/757),
[#771](https://github.com/invertase/react-native-google-mobile-ads/issues/771),
[#754](https://github.com/invertase/react-native-google-mobile-ads/issues/754)).

### FlatList, FlashList, and recycled list cells

`FlatList`, [FlashList](https://shopify.github.io/flash-list/), and similar lists **reuse cell component instances**. React state inside a recycled cell survives the row change. A card that loads once in `useEffect([])` and deliberately avoids remount `key`s will keep showing the previous row's `NativeAd` after scroll — that looks like "the same ad repeating," even when each mount once called `NativeAd.createForAdRequest`.

**Load against list-item identity** (or remount the card):

```tsx
function NativeAdCard({ itemId, adUnitId }: { itemId: string; adUnitId: string }) {
  const [nativeAd, setNativeAd] = useState<NativeAd>();

  useEffect(() => {
    let cancelled = false;
    let loaded: NativeAd | undefined;
    NativeAd.createForAdRequest(adUnitId)
      .then(ad => {
        if (cancelled) {
          ad.destroy();
          return;
        }
        loaded = ad;
        setNativeAd(ad);
      })
      .catch(console.error);
    return () => {
      cancelled = true;
      loaded?.destroy();
      setNativeAd(undefined);
    };
  }, [itemId, adUnitId]);

  if (!nativeAd) {
    return null;
  }

  return <NativeAdView nativeAd={nativeAd}>{/* assets */}</NativeAdView>;
}

// Remount path: key the card so FlashList cannot keep stale JS state for a new row.
// <NativeAdCard key={item.id} itemId={item.id} adUnitId={unitId} />
```

**Prefer a preload pool** when many feed slots need warm inventory: poll/bind a ready `NativeAd` per visible slot and `destroy()` (or return to pool policy) when the slot leaves. See
[Preload pools and multi-format recipes](/preload-pools-and-multiformat-recipes) (`AdPoolPresets.display`).

When a cell **does** reuse the same `NativeAdView` tree while you swap a different preloaded `NativeAd`, the library rebinds: `NativeAdView` passes the new `responseId`, and `NativeAsset` re-registers whenever `nativeAd.responseId` changes so SDK click handling stays attached. Prefer a stable React `key` per ad (for example `nativeAd.responseId`) when the row should remount instead of rebind.

**No uniqueness guarantee.** Each successful `createForAdRequest` is a separate load request. The ad server may still return the same creative for adjacent slots. This package cannot force distinct creatives; treat back-to-back duplicates after correct per-item load/`destroy` as fill inventory, not a view-recycling bug.

### Android: blank app task / flaky redirects after native ad click

On Android, this package does **not** build click `Intent`s or set `FLAG_ACTIVITY_NEW_TASK`.
Google Mobile Ads owns click handling once assets are registered on `NativeAdView`. The tip
constructs that SDK view with the current `Activity` (same mediation guidance as banner
`AdView`) and defers construction until an Activity is attached — a non-Activity
`ReactContext` / Application context forces the SDK to add `FLAG_ACTIVITY_NEW_TASK`, which
can leave a **blank app card in Recents** while the destination URL still opens, and make
later taps need multiple tries until that blank task is closed. Assets registered before
Activity attach are queued and flushed once the SDK `NativeAdView` is created.

Related hit-testing / registration work (AdChoices overlay elevation, clearing asset
`clickable`, FlatList rebind) is separate: see click-ownership notes earlier on this page
([Registered assets only](/ad-formats/native#registered-assets-only)).

If you still see a blank Recents instance with a current tip build:

1. Confirm `MainActivity` is resumed when the native ad mounts (cold-start trees
   defer SDK `NativeAdView` until an Activity is attached).
2. Check host `AndroidManifest.xml` `MainActivity` `android:launchMode`. React Native /
   Expo templates often use `singleTask` for deep links; that mode interacts with how
   Google's `AdActivity` / Custom Tabs and any `https` intent-filters on `MainActivity`
   share tasks. Prefer `singleTop` when compatible — see
   [Android: fullscreen ads closing after Home → resume](/ad-formats/interstitial#android-fullscreen-ads-closing-after-home--resume).
3. If an ad destination matches your app's App Links / intent-filters, the system may
   route the click into your activity instead of the browser; that is host deep-link
   configuration, not tip intent flags.
4. Re-test with Google test units on tip-pinned GMA (`packages/core/package.json`
   `sdkVersions.android`). If a minimal native AdMob sample still duplicates a blank task
   on the same device / OS / `launchMode`, escalate to Google with Mobile Ads SDK logs.

This package cannot override Google's click Intent flags or keep a host `singleTask`
manifest from clearing / stacking activities around SDK chrome.

### Not implemented features

These features are not implemented in the current version:

- Custom Ad Choices view
- Custom video playback controls
- Custom click gestures
- [Custom native ad formats](https://support.google.com/admanager/answer/6366911)
