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

# Integrating Ads

> Add ads to your overlay to monetize it.

Your overlay has an audience, and ads are our recommended way to turn that into revenue without charging users. This
guide walks through the full integration, end to end: coordinating with the Overlayed team, wiring up the main,
preload, and render processes, and getting your first ad fill on screen.

<Info>
  This guide covers the integration workflow. For API details, see the ads reference pages: [Monetize Your
  Overlay](/ads/introduction), [Ad Unit Setup](/ads/ads-setup), and [Configuration](/ads/configuration).
</Info>

## How Ads Work

Before writing any code, it helps to know four things about how Overlayed serves ads:

1. **Ad inventory is arranged with the Overlayed team first.** Ad units, their base selectors (like `htlad-medrec`),
   and where they may be positioned are agreed on before you integrate. Reach out early - the ads have to be set up on
   Overlayed's side before your code can request them.
2. **Setup spans all three Electron layers.** The main process registers each ad-enabled window
   (`overlay.ads.registerWindow`), the preload script imports `@overlayed/app/preload`, and the render process uses
   the `@overlayed/ads` package to initialize, mount the ads script, and request fills.
3. **Ads render into plain `<div>` elements in your DOM.** You place the containers in your layout and explicitly
   request fills for them with `refreshAdsViaDivMappings` - nothing renders until you ask.
4. **Calls queue until the ads script loads.** `init`, `setPageTargeting`, and `refreshAdsViaDivMappings` are safe to
   call before the script has finished loading; commands are queued and drained once the ad provider is ready.

## Wiring Up Ads

<Steps>
  <Step title="Coordinate with the Overlayed team">
    Contact the Overlayed team to discuss ads for your app. Together you'll settle the ad units available to you,
    the base selectors your app will use, and where ads will be positioned. Start this early - nothing below will
    fill until it's done.
  </Step>

  <Step title="Install the ads package">
    Add `@overlayed/ads` to your app. It's a separate package from `@overlayed/app` and runs in your render
    process.

    ```bash theme={null}
    pnpm i @overlayed/ads
    ```
  </Step>

  <Step title="Register ad-enabled windows (main process)">
    For every window you intend to show ads in, call `overlay.ads.registerWindow` after creating it:

    ```typescript main.ts theme={null}
    const hudWindow = overlay.windows.createInGameWindow({
    	width: 400,
    	height: 600,
    });

    overlay.ads.registerWindow(hudWindow);
    ```

    This works for both in-game and out-of-game windows - register each one that will contain an ad container. See
    [Creating Windows](/windows/introduction) for the two window types.
  </Step>

  <Step title="Import the preload script">
    In your app's [preload script](https://www.electronjs.org/docs/latest/tutorial/tutorial-preload), add:

    ```typescript preload.ts theme={null}
    import "@overlayed/app/preload";
    ```

    This exposes the data the ads package needs in the render process. If you skip it, `init()` fails with
    "could not find overlayed instance".
  </Step>

  <Step title="Initialize and mount the ads script (render process)">
    When your page first loads, initialize the ads package and append the ads script to the document head:

    ```typescript renderer.ts theme={null}
    import overlayedAds, { getAdsScriptElement } from "@overlayed/ads";

    overlayedAds.init();

    const script = getAdsScriptElement({
    	onError: (event) => {
    		// Wire this into your logging/observability - a failed script load means no ads at all
    		console.error("Failed to load ads script", event);
    	},
    });

    if (script) {
    	document.head.appendChild(script);
    }
    ```

    `init()` sets the `app_id` page targeting for you and is safe to call more than once. The `onError` handler is
    intentionally a required parameter - script load failure is the failure mode you most want visibility into.
  </Step>

  <Step title="Add an ad container and request a fill">
    Each ad unit is a `<div>` with an `id` that uniquely identifies the slot and a class matching one of your
    approved base selectors:

    ```html index.html theme={null}
    <div id="right-rail-2" class="htlad-medrec"></div>
    ```

    Once the container exists in the DOM, request a fill for it:

    ```typescript renderer.ts theme={null}
    import overlayedAds from "@overlayed/ads";

    overlayedAds.refreshAdsViaDivMappings([{ divId: "right-rail-2", baseDivId: ".htlad-medrec" }]);
    ```

    Note the shapes: the `class` attribute has no leading dot (`htlad-medrec`), while `baseDivId` is a selector and
    does (`.htlad-medrec`). That's it - once the script loads and the fill request resolves, the ad renders inside
    your container.
  </Step>
</Steps>

## Refreshing on Navigation

`refreshAdsViaDivMappings` is not fire-and-forget for the lifetime of the app - call it on initial mount and again
whenever the page context changes, such as a route navigation in a single-page app. Pass targeting so the ad request
reflects the new context:

```typescript theme={null}
import overlayedAds, { setPageTargeting } from "@overlayed/ads";

function onRouteChange(pageId: string) {
	setPageTargeting({ page_id: pageId });

	overlayedAds.refreshAdsViaDivMappings([
		{ divId: "right-rail-2", baseDivId: ".htlad-medrec" },
		{ divId: "right-rail-3", baseDivId: ".htlad-medrec" },
	]);
}
```

Page-level targeting from `setPageTargeting` applies to every subsequent request; per-mapping `targeting` is merged on
top for individual slots. See [Configuration](/ads/configuration) for the full breakdown.

<Warning>
  You may not pass any user-identifiable data - names, addresses, user IDs - in page-level or per-slot targeting.
</Warning>

## Supporting Ad-Free Users

There is no runtime method to toggle ads on and off. To hide ads for a user - a paid subscriber, for example - gate the
integration at its two entry points in the render process:

```typescript renderer.ts theme={null}
if (!user.isSubscriber) {
	overlayedAds.init();

	const script = getAdsScriptElement({
		onError: (event) => console.error("Failed to load ads script", event),
	});

	if (script) {
		document.head.appendChild(script);
	}
}
```

Then skip rendering the ad containers in your UI for those users. No script plus no containers means no ad requests.

<Tip>
  Keep the decision in one place (a single `adsEnabled` flag) so the script mount, the containers, and your
  `refreshAdsViaDivMappings` calls can't drift out of sync.
</Tip>

## FAQ

<AccordionGroup>
  <Accordion title="How do I know the ads script actually loaded?">
    On success, the package logs `[Overlayed] Ads script loaded successfully.` to the console. On failure, your
    `onError` callback fires and a console error is logged with the script URL. Treat `onError` as a real
    observability hook, not a formality - if the script never loads, every ad slot stays empty.
  </Accordion>

  <Accordion title="Can I call refreshAdsViaDivMappings before the script finishes loading?">
    Yes. `init`, `setPageTargeting`, and `refreshAdsViaDivMappings` all push commands onto a queue that the ad
    provider drains once it's ready. You don't need to sequence your calls against the script's `onload`.
  </Accordion>

  <Accordion title="Can I show multiple ads on one page?">
    Yes. `refreshAdsViaDivMappings` takes an array - pass one mapping per container, each with a unique `divId`.
    Which units and how many placements you can run is part of what you agree on with the Overlayed team.
  </Accordion>

  <Accordion title="Do ads work in both in-game and out-of-game windows?">
    Yes. Any `BrowserWindow` or `RenderWindow` can show ads, as long as you called
    `overlay.ads.registerWindow` for it in the main process and its page runs the render-side setup.
  </Accordion>

  <Accordion title="Does the package expose ad lifecycle events like impressions or clicks?">
    No. The `@overlayed/ads` API surface is `init`, `refreshAdsViaDivMappings`, `setPageTargeting`,
    `getAdsScriptElement`, and `getAdsScriptUrl` - there are no per-ad callbacks for impressions, clicks, or
    no-fills. The only failure signal you can hook is the script-load `onError`.
  </Accordion>

  <Accordion title="init() logs &#x22;could not find overlayed instance&#x22; - what's wrong?">
    The render process can't see the Overlayed globals, which almost always means the preload import is missing.
    Confirm your preload script contains `import "@overlayed/app/preload";` and that the window was created through
    `overlay.windows.createWindow` or `overlay.windows.createInGameWindow`.
  </Accordion>

  <Accordion title="Can I put a user ID in targeting to improve ad relevance?">
    No. Passing user-identifiable data - names, addresses, user IDs, anything of the kind - in page-level or
    per-slot targeting is prohibited. Stick to page and content context, like `page_id`.
  </Accordion>

  <Accordion title="What decides which ad sizes and units I can use?">
    The Overlayed team. Ad units, the set of base selectors your app uses, and ad positioning are all agreed on
    together before integration - there's no self-serve catalog to pick from. If you need a new unit or placement,
    that's a conversation, not a code change.
  </Accordion>

  <Accordion title="Where does the ads script URL come from?">
    It's built from your app's production site URL (the domain configured in your Overlayed Dashboard).
    `getAdsScriptElement` handles this for you; if you need the raw URL - for a CSP allowlist, for example - call
    `getAdsScriptUrl()`.
  </Accordion>
</AccordionGroup>

## Next Steps

<CardGroup cols={2}>
  <Card title="Monetize Your Overlay" icon="sack-dollar" href="/ads/introduction">
    The ads setup reference: installation, window registration, and script mounting.
  </Card>

  <Card title="Ad Unit Setup" icon="table-layout" href="/ads/ads-setup">
    Ad unit containers, base selectors, and per-slot targeting.
  </Card>

  <Card title="Ads Configuration" icon="sliders" href="/ads/configuration">
    Refreshing fills, page-level targeting, and conditionally disabling ads.
  </Card>

  <Card title="@overlayed/ads API" icon="rectangle-ad" href="/packages/ads/ads">
    Full method-by-method reference for the ads package.
  </Card>
</CardGroup>
