> ## Documentation Index
> Fetch the complete documentation index at: https://docs.adrop.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Native Ads

> Guide to implementing native ads in React Native apps.

## Overview

Native ads are an ad format that allows you to freely customize the ad UI to match your app's design. You can individually place ad elements such as media (images, videos), title, description, and CTA button to provide a natural user experience.

### Key Features

* **Complete UI Customization**: Freely configure ad layout to match your app's design system
* **Support for Various Media**: Support for image and video ad materials
* **Flexible Click Handling**: Full click or custom click handling available
* **Profile Information Display**: Support for advertiser profile logo and name
* **Backfill Ad Support**: Can check if it's a backfill ad

***

## AdropNativeAd Class

The main class for loading and managing native ads.

### Constructor

```typescript theme={null}
new AdropNativeAd(
  unitId: string,
  useCustomClick?: boolean,
  preferredAdChoicesPosition?: AdropAdChoicesPosition
)
```

<ParamField path="unitId" type="string" required>
  Ad unit ID (issued from console)
</ParamField>

<ParamField path="useCustomClick" type="boolean" default="false">
  Whether to use custom click handling

  * `true`: Developer implements click handling directly (can use video ad controller)
  * `false`: AdropNativeAdView automatically handles all click events
</ParamField>

<ParamField path="preferredAdChoicesPosition" type="AdropAdChoicesPosition" default="AdropAdChoicesPosition.topRight">
  AdChoices badge position for backfill native ads
</ParamField>

### Methods

| Method      | Description             |
| ----------- | ----------------------- |
| `load()`    | Request and load the ad |
| `destroy()` | Release ad resources    |

### Properties

| Property         | Type                    | Description                                       |
| ---------------- | ----------------------- | ------------------------------------------------- |
| `isLoaded`       | `boolean`               | Whether ad is loaded (read-only)                  |
| `unitId`         | `string`                | Ad unit ID (read-only)                            |
| `useCustomClick` | `boolean`               | Whether custom click handling is used (read-only) |
| `creativeId`     | `string`                | Ad creative ID (read-only)                        |
| `txId`           | `string`                | Transaction ID (read-only)                        |
| `campaignId`     | `string`                | Campaign ID (read-only)                           |
| `isBackfilled`   | `boolean`               | Whether it's a backfill ad (read-only)            |
| `isVideoAd`      | `boolean`               | Whether it's a video ad (read-only)               |
| `browserTarget`  | `BrowserTarget`         | Browser target setting (read-only)                |
| `properties`     | `AdropNativeProperties` | Ad properties information (read-only)             |
| `listener`       | `AdropNativeAdListener` | Ad event listener                                 |

***

## AdropNativeAdView Component

Container component to display native ads.

### Props

```typescript theme={null}
interface Props extends ViewProps {
  nativeAd?: AdropNativeAd
}
```

<ParamField path="nativeAd" type="AdropNativeAd">
  Native ad instance to display
</ParamField>

### Usage Example

```tsx theme={null}
<AdropNativeAdView nativeAd={nativeAd} style={styles.adContainer}>
  {/* Ad elements */}
</AdropNativeAdView>
```

<Note>
  `AdropNativeAdView` provides ad context, so all ad element views must be placed inside this component.
</Note>

***

## Native Ad Element Views

### AdropProfileLogoView

Image component to display advertiser profile logo.

```tsx theme={null}
<AdropProfileLogoView style={styles.profileLogo} />
```

**Props**: `Omit<ImageProps, 'source'>` (React Native Image component properties, `source` is auto-populated)

### AdropProfileNameView

Text component to display advertiser profile name.

```tsx theme={null}
<AdropProfileNameView style={styles.profileName} />
```

**Props**: `TextProps` (React Native Text component properties)

### AdropHeadLineView

Text component to display ad title.

```tsx theme={null}
<AdropHeadLineView style={styles.headline} />
```

**Props**: `TextProps` (React Native Text component properties)

### AdropBodyView

Text component to display ad body text.

```tsx theme={null}
<AdropBodyView style={styles.body} />
```

**Props**: `TextProps` (React Native Text component properties)

### AdropMediaView

Media component to display ad image or video.

```tsx theme={null}
<AdropMediaView style={styles.media} />
```

**Props**: `ViewProps` (React Native View component properties)

<Warning>
  For **video native creatives**, render `properties.creative` (the HTML payload) inside a `WebView`. Passing `properties.asset` directly into `react-native-video` or any other custom player bypasses the SDK's video tracking pipeline, so **VTR is not measured** and `onAdVideoStart` / `onAdVideoEnd` never fire. See [Video Tracking and VTR](#video-tracking-and-vtr).
</Warning>

### AdropCallToActionView

Text component to display call-to-action (CTA) message.

```tsx theme={null}
<AdropCallToActionView style={styles.cta} />
```

**Props**: `TextProps` (React Native Text component properties)

### AdropAdvertiserView

Text component to display advertiser name.

```tsx theme={null}
<AdropAdvertiserView style={styles.advertiser} />
```

**Props**: `TextProps` (React Native Text component properties)

### AdropIconView

Image component to display ad icon.

```tsx theme={null}
<AdropIconView style={styles.icon} />
```

**Props**: `Omit<ImageProps, 'source'>` (React Native Image component properties, `source` is auto-populated)

***

## AdropNativeAdListener Interface

Listener interface for handling ad events.

```typescript theme={null}
interface AdropNativeAdListener {
  onAdReceived?: (ad: AdropNativeAd) => void
  onAdClicked?: (ad: AdropNativeAd) => void
  onAdImpression?: (ad: AdropNativeAd) => void
  onAdFailedToReceive?: (ad: AdropNativeAd, errorCode?: any) => void
  onAdVideoStart?: (ad: AdropNativeAd) => void
  onAdVideoEnd?: (ad: AdropNativeAd) => void
}
```

| Callback Method       | Description                             |
| --------------------- | --------------------------------------- |
| `onAdReceived`        | Called when ad reception is successful  |
| `onAdClicked`         | Called when ad is clicked               |
| `onAdImpression`      | Called when ad is displayed             |
| `onAdFailedToReceive` | Called when ad reception fails          |
| `onAdVideoStart`      | Called when a video ad starts playing   |
| `onAdVideoEnd`        | Called when a video ad finishes playing |

***

## AdropNativeProperties Type

Type containing ad properties information.

```typescript theme={null}
type AdropNativeProperties = {
  icon?: string                      // Icon image URL
  cover?: string                     // Cover image URL
  headline?: string                  // Ad title
  body?: string                      // Ad body text
  creative?: string                  // Ad creative HTML (for WebView rendering)
  asset?: string                     // Asset information
  destinationURL?: string            // Destination URL
  advertiserURL?: string             // Advertiser URL
  accountTag?: string                // Account tag
  creativeTag?: string               // Creative tag
  advertiser?: string                // Advertiser name
  callToAction?: string              // Call to action message
  profile?: AdropNativeProfile       // Profile information
  extra?: Record<string, string>     // Additional information
  isBackfilled?: boolean             // Whether it's a backfill ad
}
```

***

## AdropNativeProfile Type

Type containing advertiser profile information.

```typescript theme={null}
type AdropNativeProfile = {
  displayName: string                // Profile display name
  displayLogo: string                // Profile logo image URL
}
```

***

## Implementation Example

Here's a complete example of implementing native ads.

```tsx theme={null}
import React, { useCallback, useEffect, useMemo, useState } from 'react'
import {
  AdropBodyView,
  AdropHeadLineView,
  AdropMediaView,
  AdropNativeAd,
  AdropNativeAdView,
  AdropProfileLogoView,
  AdropProfileNameView,
} from 'adrop-ads-react-native'
import type { AdropNativeAdListener } from 'adrop-ads-react-native'
import { WebView } from 'react-native-webview'
import {
  Button,
  Dimensions,
  ScrollView,
  StyleSheet,
  Text,
  View,
  Linking,
  Platform,
} from 'react-native'

const NativeAdExample: React.FC = () => {
  const [nativeAd, setNativeAd] = useState<AdropNativeAd>()
  const [isLoaded, setIsLoaded] = useState(false)
  const [errorCode, setErrorCode] = useState('')

  // URL opening handler
  const openUrl = useCallback((url: string) => {
    Linking.openURL(url).catch((err) =>
      console.error('Failed to open URL:', err)
    )
  }, [])

  // Ad event listener
  const listener = useMemo(
    (): AdropNativeAdListener => ({
      onAdReceived: (ad) => {
        console.log('Ad received:', ad.unitId, ad.properties)
        setIsLoaded(true)
        setErrorCode('')
      },
      onAdFailedToReceive: (_, error) => {
        console.log('Ad reception failed:', error)
        setErrorCode(error)
      },
      onAdClicked: (ad) => {
        console.log('Ad clicked:', ad.unitId)
      },
      onAdImpression: (ad) => {
        console.log('Ad impression:', ad.unitId)
      },
    }),
    []
  )

  // Initialize ad
  const initialize = useCallback(
    (unitId: string) => {
      const adropNativeAd = new AdropNativeAd(unitId)
      adropNativeAd.listener = listener

      setNativeAd((prev) => {
        prev?.destroy()
        return adropNativeAd
      })

      setIsLoaded(false)
      setErrorCode('')
    },
    [listener]
  )

  // Initialize ad on component mount
  useEffect(() => {
    initialize('YOUR_UNIT_ID')

    // Release ad on component unmount
    return () => {
      nativeAd?.destroy()
    }
  }, [])

  // Load ad
  const load = () => nativeAd?.load()

  // Render ad view
  const adView = useMemo(() => {
    if (!isLoaded) return null

    return (
      <AdropNativeAdView
        nativeAd={nativeAd}
        style={{
          ...styles.adContainer,
          width: Dimensions.get('window').width,
        }}
      >
        {/* Profile area */}
        <View style={styles.rowContainer}>
          <AdropProfileLogoView style={styles.profileLogo} />
          <AdropProfileNameView style={styles.profileName} />
        </View>

        {/* Ad title */}
        <AdropHeadLineView style={styles.headline} />

        {/* Ad body */}
        <AdropBodyView style={styles.body} />

        {/* Media view for backfilled ads or non-video ads, WebView for video ads */}
        {!nativeAd?.isVideoAd || nativeAd?.isBackfilled ? (
          // Backfill ad or non-video ad: Use AdropMediaView
          <AdropMediaView style={styles.media} />
        ) : (
          // Video ad: Render with WebView
          <WebView
            source={{
              html: nativeAd?.properties?.creative ?? '',
            }}
            style={styles.media}
            javaScriptEnabled={true}
            mediaPlaybackRequiresUserAction={false}
            allowsInlineMediaPlayback={true}
            scrollEnabled={false}
            onNavigationStateChange={(event) => {
              // Android WebView event
              if (
                event.url &&
                event.url !== 'about:blank' &&
                !event.url.startsWith('data:')
              ) {
                openUrl(event.url)
              }
            }}
            onOpenWindow={(event) => {
              // iOS WebView event (window.open)
              if (event.nativeEvent?.targetUrl) {
                openUrl(event.nativeEvent.targetUrl)
              }
            }}
          />
        )}
      </AdropNativeAdView>
    )
  }, [isLoaded, nativeAd, openUrl])

  return (
    <ScrollView>
      <View style={styles.container}>
        {/* Ad load button */}
        <View style={styles.button}>
          <Button title="Load Ad" onPress={load} />
        </View>

        {/* Ad view */}
        {adView}

        {/* Error display */}
        {errorCode && (
          <Text style={styles.error}>
            Error: {errorCode}
          </Text>
        )}
      </View>
    </ScrollView>
  )
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    alignItems: 'center',
    paddingVertical: 5,
    paddingHorizontal: 16,
  },
  button: {
    marginVertical: 8,
  },
  adContainer: {
    paddingHorizontal: 16,
  },
  rowContainer: {
    flexDirection: 'row',
    justifyContent: 'flex-start',
    alignItems: 'center',
    marginBottom: 8,
  },
  profileLogo: {
    width: 32,
    height: 32,
    marginRight: 8,
    borderRadius: 16,
  },
  profileName: {
    fontSize: 14,
    fontWeight: 'bold',
    color: 'black',
  },
  headline: {
    fontSize: 16,
    fontWeight: 'bold',
    color: 'black',
    marginTop: 8,
  },
  body: {
    fontSize: 14,
    color: 'black',
    marginVertical: 8,
  },
  media: {
    width: '100%',
    height: 360,
    marginTop: 8,
  },
  error: {
    color: 'red',
    marginVertical: 8,
  },
})

export default NativeAdExample
```

***

## Test Unit IDs

Use the following test unit IDs during development and testing.

| Platform    | Format              | Test Unit ID                            |
| ----------- | ------------------- | --------------------------------------- |
| Android/iOS | Native (Image)      | `PUBLIC_TEST_UNIT_ID_NATIVE`            |
| Android/iOS | Native Video (16:9) | `PUBLIC_TEST_UNIT_ID_NATIVE_VIDEO_16_9` |
| Android/iOS | Native Video (9:16) | `PUBLIC_TEST_UNIT_ID_NATIVE_VIDEO_9_16` |

<Warning>
  Be sure to replace with actual unit IDs before production deployment.
</Warning>

***

## Error Handling

When ad loading fails, you can receive error codes in the `onAdFailedToReceive` callback.

### Common Error Codes

| Error Code                      | Description               |
| ------------------------------- | ------------------------- |
| `AdropErrorCode.network`        | Network error             |
| `AdropErrorCode.internal`       | Internal error            |
| `AdropErrorCode.initialize`     | Initialization error      |
| `AdropErrorCode.invalidUnit`    | Invalid unit ID           |
| `AdropErrorCode.adNoFill`       | No ads available          |
| `AdropErrorCode.adLoading`      | Ad is loading             |
| `AdropErrorCode.backfillNoFill` | No backfill ads available |

### Error Handling Example

```typescript theme={null}
const listener: AdropNativeAdListener = {
  onAdFailedToReceive: (ad, errorCode) => {
    switch (errorCode) {
      case AdropErrorCode.adNoFill:
        console.log('No ads available - hide ad area')
        break
      case AdropErrorCode.network:
        console.log('Network error - retry')
        setTimeout(() => ad.load(), 3000)
        break
      default:
        console.log('Ad loading failed:', errorCode)
    }
  }
}
```

***

## Best Practices

### 1. Resource Cleanup

Always release ad resources when component unmounts.

```typescript theme={null}
useEffect(() => {
  const ad = new AdropNativeAd('YOUR_UNIT_ID')
  setNativeAd(ad)

  return () => {
    ad.destroy()
  }
}, [])
```

### 2. Video Ad and Backfill Ad Handling

Choose appropriate rendering method based on whether it's a video ad or backfill ad.

```tsx theme={null}
{!nativeAd?.isVideoAd || nativeAd?.isBackfilled ? (
  <AdropMediaView style={styles.media} />
) : (
  <WebView source={{ html: nativeAd?.properties?.creative ?? '' }} />
)}
```

### 3. URL Handling

Handle URL clicks in WebView to open in external browser.

```tsx theme={null}
<WebView
  onNavigationStateChange={(event) => {
    if (event.url && event.url !== 'about:blank') {
      Linking.openURL(event.url)
    }
  }}
/>
```

### 4. Selective Use of Ad Elements

Selectively use only the ad elements needed to match your app's design.

```tsx theme={null}
<AdropNativeAdView nativeAd={nativeAd}>
  {/* Minimal configuration: Display only media and title */}
  <AdropHeadLineView style={styles.headline} />
  <AdropMediaView style={styles.media} />
</AdropNativeAdView>
```

***

## Video Tracking and VTR

Adrop measures video metrics for native creatives — including impression-to-completion (VTR), `onAdVideoStart`, and `onAdVideoEnd` — only when the video is rendered through the SDK's media surface. On React Native, that means rendering the HTML creative (`properties.creative`) inside a `WebView`, as shown in the [Implementation Example](#implementation-example).

The `properties.asset` field returns a URL pointing to the underlying image or video file. It is exposed as supplementary metadata (for example, for thumbnails or your own analytics); it is **not** intended to drive playback.

<Warning>
  Do not feed the `properties.asset` URL into a custom video player (`react-native-video`, `expo-av`, or any third-party player) for a video native ad. When the SDK's media surface is bypassed:

  * **VTR (Video Through Rate) is not collected** for that placement.
  * `onAdVideoStart` / `onAdVideoEnd` callbacks **never fire**.
  * Aggregate video performance for the unit will under-report or report zero.

  Click (`onAdClicked`) and impression (`onAdImpression`) tracking still work because they are wired to `AdropNativeAdView`, but the video-specific signals are lost.
</Warning>

### Recommended pattern

For video creatives, render the HTML creative inside a `WebView` placed inside `AdropNativeAdView`:

```tsx theme={null}
{!nativeAd?.isVideoAd || nativeAd?.isBackfilled ? (
  <AdropMediaView style={styles.media} />
) : (
  <WebView
    source={{ html: nativeAd?.properties?.creative ?? '' }}
    style={styles.media}
    javaScriptEnabled
    mediaPlaybackRequiresUserAction={false}
    allowsInlineMediaPlayback
    scrollEnabled={false}
  />
)}
```

The `isVideoAd` and `isBackfilled` flags on `AdropNativeAd` let you branch between `<AdropMediaView />` (image or backfill creative) and `<WebView />` (direct video creative) without inspecting the asset URL yourself.

<Note>
  If a placement absolutely requires a custom player and you accept that VTR will not be measured, you can still surface click and impression attribution by keeping the ad bound to `AdropNativeAdView`. In that case, treat the placement as **non-VTR inventory** in your internal reporting and avoid mixing it with SDK-measured video performance.
</Note>

***

## AdChoices Position

For backfill native ads, you can choose which corner displays the AdChoices badge. Pass `preferredAdChoicesPosition` as the third argument to the `AdropNativeAd` constructor.

```tsx theme={null}
import { AdropNativeAd, AdropAdChoicesPosition } from 'adrop-ads-react-native'

const nativeAd = new AdropNativeAd(
  'YOUR_UNIT_ID',
  false, // useCustomClick
  AdropAdChoicesPosition.bottomRight
)

nativeAd.listener = {
  onAdReceived: (ad) => console.log('Received'),
  onAdFailedToReceive: (ad, errorCode) => console.log('Failed', errorCode),
}

nativeAd.load()
```

| Value                                | Description                |
| ------------------------------------ | -------------------------- |
| `AdropAdChoicesPosition.topLeft`     | Top-left corner            |
| `AdropAdChoicesPosition.topRight`    | Top-right corner (default) |
| `AdropAdChoicesPosition.bottomLeft`  | Bottom-left corner         |
| `AdropAdChoicesPosition.bottomRight` | Bottom-right corner        |

<Note>
  This setting is a preferred hint for the backfill network and applies only to backfill native ads — direct ads are not affected. The backfill network may override the position depending on its policy.
</Note>

***

## Next Steps

<CardGroup cols={2}>
  <Card title="Banner Ads" icon="rectangle-ad" href="/sdk/react-native/banner">
    Implement banner ads
  </Card>

  <Card title="Interstitial Ads" icon="window-maximize" href="/sdk/react-native/interstitial">
    Implement interstitial ads
  </Card>

  <Card title="Popup Ads" icon="up-right-from-square" href="/sdk/react-native/popup">
    Implement popup ads
  </Card>

  <Card title="Rewarded Ads" icon="gift" href="/sdk/react-native/rewarded">
    Implement rewarded ads
  </Card>
</CardGroup>
