Skip to main content
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.
This guide covers the integration workflow. For API details, see the ads reference pages: Monetize Your Overlay, Ad Unit Setup, and Configuration.

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

1

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

Install the ads package

Add @overlayed/ads to your app. It’s a separate package from @overlayed/app and runs in your render process.
pnpm i @overlayed/ads
3

Register ad-enabled windows (main process)

For every window you intend to show ads in, call overlay.ads.registerWindow after creating it:
main.ts
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 for the two window types.
4

Import the preload script

In your app’s preload script, add:
preload.ts
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”.
5

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:
renderer.ts
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.
6

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:
index.html
<div id="right-rail-2" class="htlad-medrec"></div>
Once the container exists in the DOM, request a fill for it:
renderer.ts
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.

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:
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 for the full breakdown.
You may not pass any user-identifiable data - names, addresses, user IDs - in page-level or per-slot targeting.

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:
renderer.ts
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.
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.

FAQ

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.
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.
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.
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.
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.
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.
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.
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.
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().

Next Steps

Monetize Your Overlay

The ads setup reference: installation, window registration, and script mounting.

Ad Unit Setup

Ad unit containers, base selectors, and per-slot targeting.

Ads Configuration

Refreshing fills, page-level targeting, and conditionally disabling ads.

@overlayed/ads API

Full method-by-method reference for the ads package.