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

# Banner Ads

> Learn how to implement banner ads in React.

## Implementation Methods

You can implement banner ads in two ways.

| Method          | Description                                      | Recommended                         |
| --------------- | ------------------------------------------------ | ----------------------------------- |
| Data Attributes | Simple implementation using HTML attributes only | Quick implementation                |
| renderAd        | Direct control over ad loading timing            | When fine-grained control is needed |

<Note>
  Use the test unit ID in development: `PUBLIC_TEST_UNIT_ID_320_100`
</Note>

***

## 1. Data Attributes Method

The simplest method - just add the `data-adrop-unit` attribute and the SDK will automatically load the ad.

```tsx theme={null}
function BannerAd() {
  return (
    <div
      data-adrop-unit="YOUR_UNIT_ID"
      data-adrop-context-id="YOUR_CONTEXT_ID"
      data-adrop-theme="light"
      data-adrop-custom-click="true"  // Enable custom click handling
    />
  );
}
```

### Data Attributes

<ParamField body="data-adrop-unit" type="string" required>
  Unit ID created in Ad Control Console
</ParamField>

<ParamField body="data-adrop-context-id" type="string">
  Context ID for [Contextual Targeting](/sdk/web/targeting#contextual-targeting)
</ParamField>

<ParamField body="data-adrop-theme" type="string" default="light">
  Theme setting (`light` or `dark`) - See [Dark Mode Settings](/ad-unit/dark-mode)
</ParamField>

<ParamField body="data-adrop-custom-click" type="boolean" default={false}>
  Enable custom click handling - When `true`, SDK will not automatically navigate to the destination URL on click, only emit `adClicked` event with destination URL. Allows developers to implement custom click behavior.
</ParamField>

<ParamField body="data-adrop-backfill-mode" type="string" default="responsive">
  Backfill ad rendering mode (`responsive` or `fixed`). See the [Backfill Rendering Mode](/sdk/web/backfill) guide for details. (SDK 1.2.3+)
</ParamField>

<ParamField body="data-adrop-ad-id" type="string">
  Advertising identifier — required for the **View on Device** feature to preview a draft creative on a registered test device. Do not hard-code in production.
</ParamField>

### Refreshing Ads

Change the React key to remount the component and request a new ad.

```tsx theme={null}
function App() {
  const [adKey, setAdKey] = useState(0);

  const reloadAd = () => {
    setAdKey(prev => prev + 1);
  };

  return (
    <>
      <div key={adKey} data-adrop-unit="YOUR_UNIT_ID" />
      <button onClick={reloadAd}>Refresh Ad</button>
    </>
  );
}
```

***

## 2. renderAd Method

Use this when you want direct control over ad loading timing.

```tsx theme={null}
import { useEffect, useRef } from 'react';
import { Adrop } from '@adrop/ads-web-sdk';

function BannerAd() {
  const containerRef = useRef<HTMLDivElement>(null);

  useEffect(() => {
    const adrop = Adrop.instance();

    if (containerRef.current) {
      adrop.renderAd(containerRef.current, {
        unit: 'YOUR_UNIT_ID',
        contextId: 'YOUR_CONTEXT_ID',
        theme: 'light',
        useCustomClick: true  // Enable custom click handling
      });
    }

  }, []);

  return <div ref={containerRef} />;
}
```

### renderAd Options

<ParamField body="unit" type="string" required>
  Unit ID created in Ad Control Console
</ParamField>

<ParamField body="uid" type="string">
  User identifier (used to override SDK-level setting for individual placements)
</ParamField>

<ParamField body="contextId" type="string">
  Context ID for [Contextual Targeting](/sdk/web/targeting#contextual-targeting)
</ParamField>

<ParamField body="theme" type="string" default="light">
  Theme setting (`light` or `dark`) - See [Dark Mode Settings](/ad-unit/dark-mode)
</ParamField>

<ParamField body="useCustomClick" type="boolean" default={false}>
  Enable custom click handling - When `true`, SDK will not automatically navigate to the destination URL on click, only emit `adClicked` event with destination URL. Allows developers to implement custom click behavior.
</ParamField>

<ParamField body="backfillMode" type="'responsive' | 'fixed'" default="responsive">
  Backfill ad rendering mode. See the [Backfill Rendering Mode](/sdk/web/backfill) guide for details. (SDK 1.2.3+)
</ParamField>

<ParamField body="adId" type="string">
  Advertising identifier — required for the **View on Device** feature to preview a draft creative on a registered test device. Do not hard-code in production.
</ParamField>

### Refreshing Ads

Call `renderAd()` again to request a new ad. Can also be used on placements created with Data Attributes.

```tsx theme={null}
function BannerAd() {
  const containerRef = useRef<HTMLDivElement>(null);

  const reloadAd = () => {
    const adrop = Adrop.instance();
    if (containerRef.current) {
      adrop.renderAd(containerRef.current, {
        unit: 'YOUR_UNIT_ID'
      });
    }
  };

  return (
    <>
      <div ref={containerRef} />
      <button onClick={reloadAd}>Refresh Ad</button>
    </>
  );
}
```

<Note>
  When calling `renderAd()` on a placement created with Data Attributes, you only need to pass items that don't exist in data attributes or items you want to modify.
</Note>

### Removing Ads

Call `clear()` to remove the ad from the container.

```tsx theme={null}
const adrop = Adrop.instance();
adrop.clear(containerRef.current);
```

***

## Ad Size

Recommended: responsive width + `aspect-ratio` matching the registered unit size. The slot scales with the device width and the height is locked by the ratio (no CLS).

```tsx theme={null}
<div
  data-adrop-unit="YOUR_UNIT_ID"
  data-adrop-backfill-mode="fixed"
  style={{ width: '100%', aspectRatio: '320 / 100' }}
/>
```

Fixed pixel size is also supported when pixel-precise alignment is required.

```tsx theme={null}
<div
  data-adrop-unit="YOUR_UNIT_ID"
  style={{ width: 320, height: 100 }}
/>
```

| Type         | Size Setting                                                                              | Alignment                  |
| ------------ | ----------------------------------------------------------------------------------------- | -------------------------- |
| Direct Ads   | Responsive support, height required                                                       | Horizontal/Vertical center |
| Backfill Ads | Auto-matched to container width (`responsive`) or stretched to container height (`fixed`) | Horizontal center          |

<Note>
  For container sizing patterns and how to choose a backfill rendering mode, see the [Backfill Rendering Mode](/sdk/web/backfill) guide.
</Note>

***

## Event Handling

You can detect ad status changes and handle the UI appropriately.

```tsx theme={null}
import { useEffect, useRef } from 'react';
import { Adrop } from '@adrop/ads-web-sdk';

function BannerAd({ unitId }: { unitId: string }) {
  const containerRef = useRef<HTMLDivElement>(null);

  useEffect(() => {
    const adrop = Adrop.instance();

    // Ad received successfully
    const handleReceived = (unit: string, adData: any) => {
      console.log('Ad received:', unit);
    };

    // No direct ads available
    const handleNoFill = (unit: string) => {
      console.warn('No ad available:', unit);
    };

    // Request failed
    const handleFailed = (unit: string) => {
      console.error('Ad failed:', unit);
    };

    // Impression
    const handleImpression = (unit: string, adData: any) => {
      console.log('Ad impression:', unit);
    };

    // Click
    const handleClicked = (unit: string, adData: any) => {
      console.log('Ad clicked:', unit);
    };

    // No backfill ad available
    const handleBackfillNoFill = (unit: string) => {
      console.warn('No backfill ad:', unit);
    };

    // Video playback started
    const handleVideoStart = (unit: string, adData: any) => {
      console.log('Video started:', unit);
    };

    // Video playback ended
    const handleVideoEnd = (unit: string, adData: any) => {
      console.log('Video ended:', unit);
    };

    // Filter for specific unit only
    const filter = { unit: unitId };

    adrop.on(Adrop.Events.AD_RECEIVED, handleReceived, filter);
    adrop.on(Adrop.Events.AD_NO_FILL, handleNoFill, filter);
    adrop.on(Adrop.Events.AD_FAILED, handleFailed, filter);
    adrop.on(Adrop.Events.AD_IMPRESSION, handleImpression, filter);
    adrop.on(Adrop.Events.AD_CLICKED, handleClicked, filter);
    adrop.on(Adrop.Events.AD_BACKFILL_NO_FILL, handleBackfillNoFill, filter);
    adrop.on(Adrop.Events.AD_VIDEO_START, handleVideoStart, filter);
    adrop.on(Adrop.Events.AD_VIDEO_END, handleVideoEnd, filter);

    if (containerRef.current) {
      adrop.renderAd(containerRef.current, { unit: unitId });
    }

    return () => {
      adrop.off(Adrop.Events.AD_RECEIVED, handleReceived);
      adrop.off(Adrop.Events.AD_NO_FILL, handleNoFill);
      adrop.off(Adrop.Events.AD_FAILED, handleFailed);
      adrop.off(Adrop.Events.AD_IMPRESSION, handleImpression);
      adrop.off(Adrop.Events.AD_CLICKED, handleClicked);
      adrop.off(Adrop.Events.AD_BACKFILL_NO_FILL, handleBackfillNoFill);
      adrop.off(Adrop.Events.AD_VIDEO_START, handleVideoStart);
      adrop.off(Adrop.Events.AD_VIDEO_END, handleVideoEnd);
    };
  }, [unitId]);

  return <div ref={containerRef} />;
}
```

### Supported Events

| Event          | Constant              | Description               |
| -------------- | --------------------- | ------------------------- |
| Ad Received    | `AD_RECEIVED`         | Ad request successful     |
| No Ad          | `AD_NO_FILL`          | No direct ads available   |
| Request Failed | `AD_FAILED`           | Ad request failed         |
| Impression     | `AD_IMPRESSION`       | Ad impression recorded    |
| Click          | `AD_CLICKED`          | User clicked the ad       |
| No Backfill    | `AD_BACKFILL_NO_FILL` | No backfill ad available  |
| Video Start    | `AD_VIDEO_START`      | Video ad playback started |
| Video End      | `AD_VIDEO_END`        | Video ad playback ended   |

<Note>
  Always remove event listeners using `off()` when the component unmounts.
</Note>

### AdData

Ad data passed to event handlers. See [Reference > AdData](/sdk/web/reference#addata) for detailed field information.

***

## Custom Click Handling

When `useCustomClick` is enabled, the SDK will not automatically navigate to the destination URL. Instead, you can handle the click event and implement custom behavior.

```tsx theme={null}
import { useEffect, useRef } from 'react';
import { Adrop } from '@adrop/ads-web-sdk';

function CustomClickBannerAd({ unitId }: { unitId: string }) {
  const containerRef = useRef<HTMLDivElement>(null);

  useEffect(() => {
    const adrop = Adrop.instance();
    const filter = { unit: unitId };

    // Handle ad click with custom behavior
    const handleClicked = (unit: string, adData: any) => {
      console.log('Ad clicked:', unit);
      console.log('Destination URL:', adData.destinationURL);
      
      // Implement custom click behavior
      // For example: show confirmation dialog, track analytics, etc.
      const userConfirmed = confirm('Do you want to visit the advertiser page?');
      if (userConfirmed && adData.destinationURL) {
        window.open(adData.destinationURL, '_blank');
      }
    };

    adrop.on(Adrop.Events.AD_CLICKED, handleClicked, filter);

    if (containerRef.current) {
      adrop.renderAd(containerRef.current, {
        unit: unitId,
        useCustomClick: true
      });
    }

    return () => {
      adrop.off(Adrop.Events.AD_CLICKED, handleClicked);
    };
  }, [unitId]);

  return <div ref={containerRef} />;
}
```

<Note>
  With custom click handling enabled, you have full control over what happens when users click the ad. The `destinationURL` is provided in the `adClicked` event data.
</Note>

***

## Complete Example

### BannerAd.tsx

```tsx theme={null}
import { useEffect, useRef } from 'react';
import { Adrop } from '@adrop/ads-web-sdk';

interface BannerAdProps {
  unitId: string;
  contextId?: string;
  theme?: 'light' | 'dark';
  onReceived?: (adData: any) => void;
  onFailed?: () => void;
  onClicked?: () => void;
}

function BannerAd({
  unitId,
  contextId,
  theme = 'light',
  onReceived,
  onFailed,
  onClicked
}: BannerAdProps) {
  const containerRef = useRef<HTMLDivElement>(null);

  useEffect(() => {
    const adrop = Adrop.instance();
    const filter = { unit: unitId };

    const handleReceived = (_: string, adData: any) => onReceived?.(adData);
    const handleFailed = () => onFailed?.();
    const handleClicked = () => onClicked?.();

    adrop.on(Adrop.Events.AD_RECEIVED, handleReceived, filter);
    adrop.on(Adrop.Events.AD_FAILED, handleFailed, filter);
    adrop.on(Adrop.Events.AD_CLICKED, handleClicked, filter);

    if (containerRef.current) {
      adrop.renderAd(containerRef.current, {
        unit: unitId,
        contextId,
        theme
      });
    }

    return () => {
      adrop.off(Adrop.Events.AD_RECEIVED, handleReceived);
      adrop.off(Adrop.Events.AD_FAILED, handleFailed);
      adrop.off(Adrop.Events.AD_CLICKED, handleClicked);
    };
  }, [unitId, contextId, theme]);

  return <div ref={containerRef} className="banner-ad-container" />;
}

export default BannerAd;
```

### Usage Example

```tsx theme={null}
import BannerAd from './components/BannerAd';

function App() {
  return (
    <div>
      <header>
        <BannerAd
          unitId="HEADER_BANNER_UNIT_ID"
          onClicked={() => console.log('Header banner clicked')}
        />
      </header>

      <main>
        <h1>Content</h1>
      </main>

      <footer>
        <BannerAd
          unitId="FOOTER_BANNER_UNIT_ID"
          theme="dark"
        />
      </footer>
    </div>
  );
}
```

***

## Backfill Ads

When backfill ads are enabled, they are automatically requested when no direct ads are available. You can distinguish ad types using `adData.format`.

```tsx theme={null}
import { useEffect, useRef } from 'react';
import { Adrop } from '@adrop/ads-web-sdk';

function BannerAd({ unitId }: { unitId: string }) {
  const containerRef = useRef<HTMLDivElement>(null);

  useEffect(() => {
    const adrop = Adrop.instance();
    const filter = { unit: unitId };

    // Ad received (direct or backfill)
    const handleReceived = (unit: string, adData: any) => {
      if (adData.format === 'backfill') {
        console.log('Backfill ad loaded');
      } else {
        console.log('Direct ad loaded');
      }
    };

    // No direct ad (backfill request starts)
    const handleNoFill = (unit: string) => {
      console.log('No direct ad available, requesting backfill...');
    };

    // No backfill ad either
    const handleBackfillNoFill = (unit: string) => {
      console.warn('No backfill ad available either');
      // Hide ad area, etc.
    };

    adrop.on(Adrop.Events.AD_RECEIVED, handleReceived, filter);
    adrop.on(Adrop.Events.AD_NO_FILL, handleNoFill, filter);
    adrop.on(Adrop.Events.AD_BACKFILL_NO_FILL, handleBackfillNoFill, filter);

    if (containerRef.current) {
      adrop.renderAd(containerRef.current, { unit: unitId });
    }

    return () => {
      adrop.off(Adrop.Events.AD_RECEIVED, handleReceived);
      adrop.off(Adrop.Events.AD_NO_FILL, handleNoFill);
      adrop.off(Adrop.Events.AD_BACKFILL_NO_FILL, handleBackfillNoFill);
    };
  }, [unitId]);

  return <div ref={containerRef} />;
}
```

<Note>
  **Development Environment Testing**: If at least one ad unit connected to the app is **Monetizing**, backfill ad requests can be made from localhost. On unapproved domains, an `AD_BACKFILL_NO_FILL` event will be triggered.
</Note>
