Render AdMob ads as real DOM elements inside your web UI by bridging the Google Mobile Ads SDK to the Despia WebView. No native overlays, no z-index fights, just AdSense, GPT, or IMA tags loading in place.
Use this file to discover all available pages before exploring further.
Inline Ads connects the Google Mobile Ads SDK directly to the Android WebView and iOS WKWebView that Despia bundles inside your app. When your web content loads ad tags through AdSense, Google Publisher Tag, or IMA for HTML5, the SDK intercepts those events and bridges native app signals into the auction. Ads render as DOM elements where you place them, not as native overlays sitting above or below the WebView.
The Despia runtime registers the WebView with the Google Mobile Ads SDK at app startup. On Android it calls MobileAds.registerWebView(webView), and on iOS it calls MobileAds.shared.register(webView). Once registered, any AdSense, GPT, or IMA tag that loads inside the WebView can communicate with the SDK and pass app-level signals (device ID, audience data) into the ad request, improving CPMs and fill rates.There is no JavaScript activation call. The bridge is always on inside the Despia runtime.
Communication with the Mobile Ads SDK only happens in response to ad events fired by AdSense, GPT, or IMA. There is no background tracking outside of those events.
Configure AdMob and your ad inventory source, then connect everything to Despia. The steps go in order, each one feeds the next.
1
Create a Google AdMob account
Go to admob.google.com and sign in with your Google account, or create one. Complete the Account setup wizard with your country, time zone, and payment profile under Payments > Settings.
2
Add your iOS app to AdMob
In AdMob, go to Apps > Add app, choose iOS, then either search for your app on the App Store or select No, my app isn’t listed in a supported app store yet and enter the name manually. AdMob issues an App ID in the format ca-app-pub-XXXXXXXXXXXXXXXX~YYYYYYYYYY.
3
Add your Android app to AdMob
Repeat Apps > Add app, this time choosing Android. AdMob requires a separate app entry per platform, each one gets its own AdMob App ID.
4
Set up your ad inventory source
Inline Ads is the SDK bridge, the actual ads come from a separate Google product depending on which tag system you embed. Configure the one matching your plan:
AdSense (most common): sign up at adsense.google.com, get your publisher ID ca-pub-XXXXXXXXXXXXXXXX, then in AdMob go to Apps > [your app] > App settings > Linked accounts and link the AdSense account so payments share a single profile.
Google Ad Manager (GPT): configure inventory at admanager.google.com, note your network code, then create ad units to reference in your tags.
IMA video ads: prepare your VAST or VMAP ad tag URL from your video ad provider.
5
Configure GDPR and CCPA consent
In AdMob, go to Privacy & messaging > GDPR, create a consent message, and target all EU and UK traffic. Repeat under CCPA for California traffic. AdMob will not serve personalized ads to those regions without it, and your inventory source (AdSense or Ad Manager) may also require its own consent flow inside the WebView.
6
Enable AdMob in the Despia Editor
Open the Despia Editor and go to App > Integrations > AdMob. Toggle the integration on, then paste the App IDs from AdMob into the matching fields:
iOS App ID (with ~)
Android App ID (with ~)
Inline Ads only needs the App IDs because actual inventory comes from your AdSense, GAM, or IMA tags embedded in your web content.
7
Rebuild your app
Trigger a fresh build from the Despia Editor. The Mobile Ads SDK has to be compiled into the app binary and registered with the WebView at startup, so this cannot be applied over-the-air. Configure once, rebuild once, and the bridge is active for the life of the build.
Skipping the rebuild leaves Inline Ads inactive even if the toggle reads enabled. The WebView API for Ads cannot be activated over-the-air. If you flip the switch and ads do not render, rebuild before opening a support ticket.
When you rebuild with Inline Ads enabled, Despia adds the manifest entries, plist keys, and WebView initialization code on both platforms. You do not write any of this yourself, but it is shown here for transparency.
Android
iOS
Despia adds the integration manager metadata to AndroidManifest.xml:
Requirements met automatically: Google Mobile Ads SDK 20.6.0 or later, Android API 21 or later, third-party cookies enabled, JavaScript and DOM storage enabled, video autoplay without user gesture enabled.
Despia adds the integration manager key to Info.plist:
The same pattern applies to Vue (onMounted), Svelte (onMount), Angular (ngAfterViewInit), and any other framework. The rule is to push to window.adsbygoogle after the container is in the DOM.See the AdSense setup guide for full configuration.
Ads play inline with no fullscreen takeover. The playsinline attribute is required for iOS, and Despia configures the WebView so autoplay works without a user gesture on both platforms.
Any banner positioned at the top or bottom of the viewport must account for the status bar, notch, and home indicator on modern phones. Despia injects --safe-area-top and --safe-area-bottom into every WebView, and the canonical pattern pairs the runtime variable with the native env() value as a fallback, then combines both with your base padding inside calc().
/* Top, status bar and notch region */padding-top: calc(1rem + var(--safe-area-top, env(safe-area-inset-top, 0px)));/* Bottom, home indicator region */padding-bottom: calc(1rem + var(--safe-area-bottom, env(safe-area-inset-bottom, 0px)));
The var() half is the source of truth inside the Despia runtime. The env() half is the native WebKit value used during web preview or before the runtime injects its variables. The 0px fallback inside env() keeps the calc() valid when neither lookup resolves. Apply the inset to the wrapping container, never to the <ins> element directly.
Without these insets, top banners render under the status bar and bottom banners are partially hidden behind the home indicator on modern iPhones. Always verify safe area behavior on a physical device or simulator running your Despia-built app, not in a desktop browser.
In a typical WebView app, the native chrome sits outside the WebView, so placing an ad between your nav bar and your content normally requires a native overlay. With Inline Ads the banner is just a flex child rendered as a DOM element directly beneath your web nav bar.
The classic mobile placement, a persistent leaderboard anchored to the bottom of the viewport. In a pure WebView app this normally requires a native view rendered outside the WebView. Here it is just position: sticky plus the safe area inset.
Ads interspersed between list items, the pattern used by Instagram, Twitter, and most news apps. Replace every nth card with a fluid in-article unit so the ad sizes itself to match surrounding content.
A full-screen ad rendered as a fixed-position overlay inside the WebView, triggered by JavaScript at a natural break point. A countdown runs before the close button appears.
AdSense and Ad Manager policy require interstitial-style placements to be clearly dismissible. Always include a visible close button. Review the AdSense program policies before deploying.
A video ad that plays before your main content video, the YouTube model. Video.js handles the player and the IMA plugin handles the ad request. The user can skip after 5 seconds (if your tag allows it) and the content video starts automatically.
Despia configures mediaPlaybackRequiresUserGesture = false on Android and mediaTypesRequiringUserActionForPlayback = [] on iOS, so video ads autoplay without requiring a tap.
Show an ad in exchange for unlocking gated content (a premium article, extra lives, a download). The user opts in, watches the ad, and your UI programmatically reveals the locked content.
React
HTML
import { useState } from 'react'function AdGate({ onUnlock }) { const [watching, setWatching] = useState(false) const [countdown, setCountdown] = useState(null) function startAd() { setWatching(true); (window.adsbygoogle = window.adsbygoogle || []).push({}); let n = 10 setCountdown(n) const timer = setInterval(() => { n-- setCountdown(n) if (n <= 0) { clearInterval(timer); onUnlock() } }, 1000) } return ( <div style={{ border: '2px dashed #ccc', borderRadius: 12, padding: 24, textAlign: 'center', }}> <p style={{ fontSize: 18, fontWeight: 600 }}>Premium content (locked)</p> <p style={{ color: '#666' }}>Watch a short ad to read this article for free.</p> {!watching ? ( <button onClick={startAd} style={{ background: '#4CAF50', color: '#fff', border: 'none', borderRadius: 6, padding: '10px 24px', fontSize: 16, cursor: 'pointer', }}> Watch ad to unlock </button> ) : ( <div> <ins className="adsbygoogle" style={{ display: 'block', width: 320, height: 250, margin: '16px auto' }} data-ad-client="ca-pub-XXXXXXXXXXXXXXXX" data-ad-slot="YYYYYYYYYY" /> <p style={{ color: '#666' }}> {countdown > 0 ? `Unlocking in ${countdown}s` : 'Unlocked'} </p> </div> )} </div> )}export default function PremiumArticle() { const [unlocked, setUnlocked] = useState(false) return ( <article style={{ maxWidth: 680, margin: '0 auto', padding: 16 }}> <h1>The Secret to 10x Growth</h1> <p>Here is the introduction everyone can read.</p> {unlocked ? ( <p>Here is the premium content, fully visible after the ad.</p> ) : ( <AdGate onUnlock={() => setUnlocked(true)} /> )} </article> )}
<script async src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js" data-ad-client="ca-pub-XXXXXXXXXXXXXXXX"></script><style> article { max-width: 680px; margin: 0 auto; padding: 16px; } #ad-gate { border: 2px dashed #ccc; border-radius: 12px; padding: 24px; text-align: center; } #premium-content { display: none; } #ad-unit { display: none; margin: 16px auto; width: 320px; height: 250px; } #unlock-btn { background: #4CAF50; color: #fff; border: none; border-radius: 6px; padding: 10px 24px; font-size: 16px; cursor: pointer; }</style><article> <h1>The Secret to 10x Growth</h1> <p>Here is the introduction everyone can read.</p> <div id="ad-gate"> <p style="font-size:18px;font-weight:600">Premium content (locked)</p> <p style="color:#666">Watch a short ad to read this article for free.</p> <button id="unlock-btn" onclick="startAd()">Watch ad to unlock</button> <ins id="ad-unit" class="adsbygoogle" style="display:block" data-ad-client="ca-pub-XXXXXXXXXXXXXXXX" data-ad-slot="YYYYYYYYYY"></ins> <p id="countdown-label" style="color:#666;display:none"></p> </div> <div id="premium-content"> <p>Here is the premium content, fully visible after the ad.</p> </div></article><script> function startAd() { document.getElementById('unlock-btn').style.display = 'none'; document.getElementById('ad-unit').style.display = 'block'; (adsbygoogle = window.adsbygoogle || []).push({}); let n = 10; const label = document.getElementById('countdown-label'); label.style.display = 'block'; label.textContent = `Unlocking in ${n}s`; const timer = setInterval(() => { n--; if (n <= 0) { clearInterval(timer); label.textContent = 'Unlocked'; setTimeout(unlock, 500); } else { label.textContent = `Unlocking in ${n}s`; } }, 1000); } function unlock() { document.getElementById('ad-gate').style.display = 'none'; document.getElementById('premium-content').style.display = 'block'; }</script>
On tablet-sized screens or landscape layouts, render a persistent unit in a side rail next to your content, the format common on desktop web but rare in mobile apps. With Inline Ads it is just a flex column.
Combine with @media (max-width: 599px) { .sidebar-ad { display: none; } } so the rail only appears on tablets and landscape phones, with a banner ad as the portrait fallback.
The Google WebView Ads test page verifies that your WebView is correctly registered with the Mobile Ads SDK. The test must run inside your app’s WebView context, so opening the URL in a desktop browser will always show a failed connection.The recommended approach is to drop a hidden link onto an existing page (profile, settings, debug menu) and tap it from your built app. Despia allows the Google test URL to load inside the WebView specifically for this purpose.
<a href="https://google.github.io/webview-ads/test/#api-for-ads-tests" style="font-size:11px;color:#999;"> Ad SDK debug</a>
A green status bar for each item confirms the integration is live.
Test
What it verifies
WebView connected to GMA SDK
Core SDK registration succeeded
JavaScript enabled
JS is enabled in the WebView
DOM storage enabled
localStorage is available
First-party cookies
1P cookies are set and readable
Third-party cookies
3P cookies work (Android)
Google Publisher Tag connected
The GPT to SDK bridge is active
If the WebView connected status fails, confirm you rebuilt the app after enabling Inline Ads. SDK registration requires a native rebuild and cannot be applied over-the-air. Hide or remove the debug link before shipping to production.
Inline Ads does not automatically propagate consent signals collected in the native app context (IAB TCF v2.0 or IAB CCPA) to the tags running inside the WebView. If you need a unified consent flow, work with your Consent Management Platform to gather consent directly in the WebView, or implement a JavaScript bridge that passes the TCF or CCPA strings from your native app into the WebView before ads load.
For users in the EEA (GDPR) or US states with privacy laws, your web ad tags must collect consent independently. The native UMP consent flow does not extend into WebView contexts on its own.
Do I need a separate AdMob App ID for iOS and Android?
Yes. AdMob requires separate App IDs for each platform. Add both in the Despia Editor and the correct one is selected per build.
Can I use Ad Manager (GAM) instead of AdSense?
Yes. Inline Ads supports Google Publisher Tag, which works with both AdSense and Ad Manager. Use your GAM ad unit paths when defining slots.
Will ads slow down my WebView?
Minimal impact. The SDK only communicates with AdMob servers in response to ad events. There is no background polling or persistent connection. Standard WebView caching and lazy loading apply as normal.
Can I show rewarded or interstitial AdMob ads from inside the WebView?
Inline Ads is designed for display, banner, and video formats embedded in web content via AdSense, GPT, or IMA. For native rewarded and interstitial formats, trigger them from the native app layer instead.
Why do I need to rebuild after toggling the setting?
The Mobile Ads SDK has to be compiled into your app binary and registered with the WebView at startup. This cannot be done over-the-air. Despia handles all the native code, you just trigger the rebuild.