Skip to main content

Documentation Index

Fetch the complete documentation index at: https://setup.despia.com/llms.txt

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.

Installation

npm install despia-native
import despia from 'despia-native';

How it works

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.
<script async src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"
    data-ad-client="ca-pub-XXXXXXXXXXXXXXXX"></script>

<ins class="adsbygoogle"
     style="display:block"
     data-ad-client="ca-pub-XXXXXXXXXXXXXXXX"
     data-ad-slot="YYYYYYYYYY"
     data-ad-format="auto"
     data-full-width-responsive="true"></ins>
<script>(adsbygoogle = window.adsbygoogle || []).push({});</script>
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.

AdMob and Despia setup

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.

What Despia configures automatically

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.
Despia adds the integration manager metadata to AndroidManifest.xml:
AndroidManifest.xml
<meta-data
    android:name="com.google.android.gms.ads.INTEGRATION_MANAGER"
    android:value="webview" />
The WebView is configured and registered on the main thread in onCreate():
MainActivity.kt
import android.webkit.CookieManager
import android.webkit.WebView
import com.google.android.gms.ads.MobileAds

CookieManager.getInstance().setAcceptThirdPartyCookies(webView, true)
webView.settings.javaScriptEnabled = true
webView.settings.domStorageEnabled = true
webView.settings.mediaPlaybackRequiresUserGesture = false

MobileAds.registerWebView(webView)
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.

Ad tag options

The bridge works with three Google ad systems. Pick the one that matches your monetization stack.

AdSense

The simplest option. Add the loader script once in <head> and place <ins> units anywhere in the body.
<script async src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"
    data-ad-client="ca-pub-XXXXXXXXXXXXXXXX"></script>

<ins class="adsbygoogle"
     style="display:block"
     data-ad-client="ca-pub-XXXXXXXXXXXXXXXX"
     data-ad-slot="YYYYYYYYYY"
     data-ad-format="auto"
     data-full-width-responsive="true"></ins>
<script>(adsbygoogle = window.adsbygoogle || []).push({});</script>
In React, push the unit after the container mounts so AdSense measures the correct width:
import { useEffect, useRef } from 'react'

function AdUnit({ slot }) {
    const ref = useRef(null)

    useEffect(() => {
        if (ref.current && ref.current.offsetWidth > 0) {
            (window.adsbygoogle = window.adsbygoogle || []).push({})
        }
    }, [])

    return (
        <ins
            ref={ref}
            className="adsbygoogle"
            style={{ display: 'block' }}
            data-ad-client="ca-pub-XXXXXXXXXXXXXXXX"
            data-ad-slot={slot}
            data-ad-format="auto"
            data-full-width-responsive="true"
        />
    )
}
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.

Google Publisher Tag

Use GPT for Ad Manager reservation and auction inventory. The bridge activates as soon as the WebView is registered.
<script async src="https://securepubads.g.doubleclick.net/tag/js/gpt.js"></script>
<script>
  window.googletag = window.googletag || { cmd: [] };
  googletag.cmd.push(() => {
    googletag
      .defineSlot('/YOUR_NETWORK_CODE/YOUR_AD_UNIT', [300, 250], 'ad-container')
      .addService(googletag.pubads());
    googletag.pubads().enableSingleRequest();
    googletag.enableServices();
    googletag.display('ad-container');
  });
</script>
<div id="ad-container" style="width:300px;height:250px;"></div>
See the Google Publisher Tag reference.

IMA for HTML5

Use IMA with Video.js for pre-roll, mid-roll, and post-roll video ads. Load dependencies in this exact order:
<link href="https://vjs.zencdn.net/8.23.8/video-js.min.css" rel="stylesheet" />
<script src="https://vjs.zencdn.net/8.23.8/video.min.js"></script>

<script src="https://imasdk.googleapis.com/js/sdkloader/ima3.js"></script>

<link href="https://cdnjs.cloudflare.com/ajax/libs/videojs-contrib-ads/7.5.2/videojs.ads.min.css" rel="stylesheet" />
<script src="https://cdnjs.cloudflare.com/ajax/libs/videojs-contrib-ads/7.5.2/videojs.ads.min.js"></script>

<link href="https://cdnjs.cloudflare.com/ajax/libs/videojs-ima/2.4.0/videojs.ima.css" rel="stylesheet" />
<script src="https://cdnjs.cloudflare.com/ajax/libs/videojs-ima/2.4.0/videojs.ima.min.js"></script>

<video id="my-video" class="video-js vjs-default-skin" controls playsinline>
  <source src="YOUR_VIDEO_URL.mp4" type="video/mp4" />
</video>

<script>
  var player = videojs('my-video');
  player.ima({ adTagUrl: 'YOUR_AD_TAG_URL' });
</script>
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.

Safe area handling

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.
<style>
  body { margin: 0; display: flex; flex-direction: column; height: 100vh; }

  nav {
    height: 56px;
    background: #1a1a2e;
    color: #fff;
    display: flex;
    align-items: center;
    padding-top:    calc(0.5rem + var(--safe-area-top,    env(safe-area-inset-top,    0px)));
    padding-left:  1rem;
    padding-right: 1rem;
    padding-bottom: 0.5rem;
    flex-shrink: 0;
  }

  .ad-bottom {
    width: 100%;
    background: #fff;
    border-top: 1px solid #e0e0e0;
    flex-shrink: 0;
    padding-top:    1rem;
    padding-bottom: calc(1rem + var(--safe-area-bottom, env(safe-area-inset-bottom, 0px)));
  }

  main { flex: 1; overflow-y: auto; padding: 1rem; }
</style>

<script async src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"
    data-ad-client="ca-pub-XXXXXXXXXXXXXXXX"></script>

<nav>My App</nav>

<main><h1>Your content</h1></main>

<div class="ad-bottom">
  <ins class="adsbygoogle"
       style="display:block"
       data-ad-client="ca-pub-XXXXXXXXXXXXXXXX"
       data-ad-slot="ZZZZZZZZZZ"
       data-ad-format="auto"
       data-full-width-responsive="true"></ins>
  <script>(adsbygoogle = window.adsbygoogle || []).push({});</script>
</div>
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.

Sticky banner under the navigation bar

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.
export default function App() {
  return (
    <div style={{ display: 'flex', flexDirection: 'column', height: '100vh' }}>

      <nav style={{
        height: 56,
        background: '#1a1a2e',
        color: '#fff',
        display: 'flex',
        alignItems: 'center',
        padding: '0 16px',
        paddingTop: 'calc(0.5rem + var(--safe-area-top, env(safe-area-inset-top, 0px)))',
        flexShrink: 0,
      }}>
        <span>My App</span>
      </nav>

      <div style={{ width: '100%', overflow: 'hidden', flexShrink: 0 }}>
        <ins
          className="adsbygoogle"
          style={{ display: 'block' }}
          data-ad-client="ca-pub-XXXXXXXXXXXXXXXX"
          data-ad-slot="YYYYYYYYYY"
          data-ad-format="auto"
          data-full-width-responsive="true"
        />
        <script>{`(adsbygoogle = window.adsbygoogle || []).push({});`}</script>
      </div>

      <main style={{ flex: 1, overflowY: 'auto', padding: 16 }}>
        <h1>Welcome</h1>
        <p>Your page content here.</p>
      </main>

    </div>
  )
}
Use data-full-width-responsive="true" so the banner fills the device width like a native banner unit.

Sticky banner pinned to the bottom

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.
export default function BottomBannerLayout({ children }) {
  return (
    <div style={{ display: 'flex', flexDirection: 'column', height: '100vh' }}>

      <main style={{ flex: 1, overflowY: 'auto', padding: 16 }}>
        {children}
      </main>

      <div style={{
        position: 'sticky',
        bottom: 0,
        width: '100%',
        background: '#fff',
        borderTop: '1px solid #e0e0e0',
        flexShrink: 0,
        paddingBottom: 'calc(1rem + var(--safe-area-bottom, env(safe-area-inset-bottom, 0px)))',
        paddingTop: '1rem',
      }}>
        <ins
          className="adsbygoogle"
          style={{ display: 'block' }}
          data-ad-client="ca-pub-XXXXXXXXXXXXXXXX"
          data-ad-slot="YYYYYYYYYY"
          data-ad-format="auto"
          data-full-width-responsive="true"
        />
        <script>{`(adsbygoogle = window.adsbygoogle || []).push({});`}</script>
      </div>

    </div>
  )
}

In-feed banner between content cards

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.
const posts = [
    { id: 1, title: 'Post one' },
    { id: 2, title: 'Post two' },
    { id: 3, title: 'Post three' },
    { id: 4, title: 'Post four' },
    { id: 5, title: 'Post five' },
    { id: 6, title: 'Post six' },
]

function AdCard() {
  return (
    <div style={{ margin: '12px 0', background: '#f9f9f9', borderRadius: 8 }}>
      <ins
        className="adsbygoogle"
        style={{ display: 'block' }}
        data-ad-client="ca-pub-XXXXXXXXXXXXXXXX"
        data-ad-slot="YYYYYYYYYY"
        data-ad-format="fluid"
        data-ad-layout="in-article"
      />
      <script>{`(adsbygoogle = window.adsbygoogle || []).push({});`}</script>
    </div>
  )
}

export default function Feed() {
  return (
    <div style={{ padding: 16 }}>
      {posts.map((post, index) => (
        <>
          {index > 0 && index % 3 === 0 && <AdCard key={`ad-${index}`} />}

          <div key={post.id} style={{
            padding: 16, marginBottom: 12,
            border: '1px solid #e0e0e0', borderRadius: 8,
          }}>
            <h3>{post.title}</h3>
          </div>
        </>
      ))}
    </div>
  )
}
data-ad-format="fluid" with data-ad-layout="in-article" produces a unit that auto-sizes to blend between content items.

Interstitial-style overlay

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.
import { useState, useEffect } from 'react'

function InterstitialAd({ onClose }) {
  const [countdown, setCountdown] = useState(5)

  useEffect(() => {
    (window.adsbygoogle = window.adsbygoogle || []).push({})

    const timer = setInterval(() => {
      setCountdown(c => {
        if (c <= 1) { clearInterval(timer); return 0 }
        return c - 1
      })
    }, 1000)

    return () => clearInterval(timer)
  }, [])

  return (
    <div style={{
      position: 'fixed', inset: 0, zIndex: 9999,
      background: 'rgba(0,0,0,0.92)',
      display: 'flex', flexDirection: 'column',
      alignItems: 'center', justifyContent: 'center',
    }}>
      <div style={{ position: 'absolute', top: 16, right: 16 }}>
        {countdown === 0 ? (
          <button onClick={onClose} style={{
            background: '#fff', border: 'none', borderRadius: 4,
            padding: '6px 14px', cursor: 'pointer', fontWeight: 600,
          }}>
            Close
          </button>
        ) : (
          <span style={{ color: '#aaa', fontSize: 14 }}>
            Close in {countdown}s
          </span>
        )}
      </div>

      <ins
        className="adsbygoogle"
        style={{ display: 'block', width: 320, height: 480 }}
        data-ad-client="ca-pub-XXXXXXXXXXXXXXXX"
        data-ad-slot="YYYYYYYYYY"
      />
    </div>
  )
}

export default function App() {
  const [showAd, setShowAd] = useState(false)

  return (
    <div style={{ padding: 16 }}>
      {showAd && <InterstitialAd onClose={() => setShowAd(false)} />}
      <h1>Article complete</h1>
      <button onClick={() => setShowAd(true)}>Next article</button>
    </div>
  )
}
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.

In-article banner

A unit that sits naturally between paragraphs of long-form content, the format used by major publishers. It scrolls with the article body.
function InArticleAd() {
  return (
    <div style={{ margin: '24px 0', textAlign: 'center' }}>
      <p style={{ fontSize: 10, color: '#999', marginBottom: 4 }}>Advertisement</p>
      <ins
        className="adsbygoogle"
        style={{ display: 'block', textAlign: 'center' }}
        data-ad-client="ca-pub-XXXXXXXXXXXXXXXX"
        data-ad-slot="YYYYYYYYYY"
        data-ad-format="fluid"
        data-ad-layout="in-article"
      />
      <script>{`(adsbygoogle = window.adsbygoogle || []).push({});`}</script>
    </div>
  )
}

export default function Article() {
  return (
    <article style={{ maxWidth: 680, margin: '0 auto', padding: 16 }}>
      <h1>The Future of Mobile Apps</h1>

      <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit.</p>
      <p>Ut enim ad minim veniam, quis nostrud exercitation ullamco.</p>

      <InArticleAd />

      <p>Duis aute irure dolor in reprehenderit in voluptate.</p>
      <p>Excepteur sint occaecat cupidatat non proident.</p>
    </article>
  )
}

Pre-roll video ad

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.
import { useEffect, useRef } from 'react'

export default function VideoWithPreroll() {
  const videoRef = useRef(null)

  useEffect(() => {
    if (!videoRef.current) return

    const player = window.videojs(videoRef.current, {
      controls: true,
      autoplay: true,
      muted: true,
      playsinline: true,
    })

    player.ima({
      adTagUrl: 'https://pubads.g.doubleclick.net/gampad/ads?YOUR_AD_TAG_PARAMS',
    })

    return () => player.dispose()
  }, [])

  return (
    <div style={{ padding: 16 }}>
      <h2>Watch: Product Overview</h2>
      <div data-vjs-player>
        <video
          ref={videoRef}
          className="video-js vjs-default-skin vjs-big-play-centered"
          style={{ width: '100%', borderRadius: 8 }}
          playsinline
        >
          <source src="https://example.com/your-content-video.mp4" type="video/mp4" />
        </video>
      </div>
    </div>
  )
}
Despia configures mediaPlaybackRequiresUserGesture = false on Android and mediaTypesRequiringUserActionForPlayback = [] on iOS, so video ads autoplay without requiring a tap.

Rewarded “unlock content” ad

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

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.
function SidebarAd() {
  return (
    <aside style={{
      width: 160,
      flexShrink: 0,
      marginLeft: 16,
      position: 'sticky',
      top: 16,
      alignSelf: 'flex-start',
    }}>
      <p style={{ fontSize: 10, color: '#999', marginBottom: 4 }}>Ad</p>
      <ins
        className="adsbygoogle"
        style={{ display: 'block', width: 160, height: 600 }}
        data-ad-client="ca-pub-XXXXXXXXXXXXXXXX"
        data-ad-slot="YYYYYYYYYY"
      />
      <script>{`(adsbygoogle = window.adsbygoogle || []).push({});`}</script>
    </aside>
  )
}

export default function TabletLayout({ children }) {
  return (
    <div style={{ display: 'flex', padding: 16, maxWidth: 960, margin: '0 auto' }}>
      <main style={{ flex: 1, minWidth: 0 }}>{children}</main>
      <SidebarAd />
    </div>
  )
}
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.

Testing your integration

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.
export default function DebugAdLink() {
  return (
    <a
      href="https://google.github.io/webview-ads/test/#api-for-ads-tests"
      style={{ fontSize: 11, color: '#999' }}
    >
      Ad SDK debug
    </a>
  )
}
A green status bar for each item confirms the integration is live.
TestWhat it verifies
WebView connected to GMA SDKCore SDK registration succeeded
JavaScript enabledJS is enabled in the WebView
DOM storage enabledlocalStorage is available
First-party cookies1P cookies are set and readable
Third-party cookies3P cookies work (Android)
Google Publisher Tag connectedThe 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.

Frequently asked questions

Yes. AdMob requires separate App IDs for each platform. Add both in the Despia Editor and the correct one is selected per build.
Yes. Inline Ads supports Google Publisher Tag, which works with both AdSense and Ad Manager. Use your GAM ad unit paths when defining slots.
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.
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.
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.

Resources

NPM Package

despia-native

AdSense setup

Official AdSense code setup guide

Google Publisher Tag

GPT reference for Ad Manager publishers

IMA for HTML5

Interactive Media Ads SDK for HTML5 video

videojs-ima plugin

Open-source IMA plus Video.js integration

iOS WebView API

Google’s official iOS WebView API for Ads docs

Android WebView API

Google’s official Android WebView API for Ads docs