Capacitor AdMob plugin with an Android-first public surface for consent, standard ad formats, native host placements, and inline banner host placements.
This package is designed for public npm distribution, conservative policy defaults, and a clean runtime contract that does not depend on app-specific UI assumptions.
- Android standard ads: banner, interstitial, rewarded, and app open
- Android Google UMP consent flow
- Android host-based native ad engine
- Android host-based inline adaptive banner engine
- Shared TypeScript event and runtime diagnostics surface
- iOS scaffold present for future parity work
| Platform | Status |
|---|---|
| Android | Implemented and build-verified |
| iOS | Scaffold only, not feature-parity complete yet |
| Web | No runtime implementation |
If you need production behavior today, treat this package as Android-first.
Current plugin support:
| Format | Android | Notes |
|---|---|---|
| Banner | Supported | Anchored banner display for top or bottom placement |
| Interstitial | Supported | Standard fullscreen interstitial |
| Rewarded | Supported | Rewarded ad with reward-earned event |
| Rewarded Interstitial | Supported | Fullscreen rewarded interstitial with reward-earned event |
| App Open | Supported | App open fullscreen format |
| Native | Supported | Host-based native placement engine |
| Native Video | Supported through native host options | Use preloadNative() / attachNative() with mediaMode: "video_preferred" |
| Inline Banner | Supported | Host-based inline adaptive banner placement |
Quick format guidance for app consumers:
- use
Rewardedwhen the user explicitly chooses to watch an ad for a reward - use
Rewarded Interstitialwhen you still need an explicit reward flow, but want the fullscreen rewarded-interstitial format instead of standard rewarded - use
Interstitialfor natural transitions without reward - use
Nativefor feed cards or embedded placement surfaces - use
NativewithmediaMode: "video_preferred"when the placement should prefer video-capable native creatives - use
Inline Bannerfor scrolling content placements where the host position comes from the WebView layout
| Method | Purpose |
|---|---|
configure(options) |
Enable or disable the plugin runtime and register placement mappings |
configureRequest(options) |
Apply global ad-request configuration |
getRuntimeInfo() |
Inspect current runtime and slot diagnostics |
clearAll() |
Clear in-memory ad state and active host-based state |
| Method | Purpose |
|---|---|
requestConsentInfo() |
Refresh consent information |
showConsentFormIfRequired() |
Show UMP consent form when required |
showPrivacyOptions() |
Show privacy options form |
getConsentStatus() |
Read current consent snapshot |
resetConsentForTesting() |
Reset consent state for development/testing |
getTrackingAuthorizationStatus() |
Read tracking authorization status |
requestTrackingAuthorization() |
Request tracking authorization |
| Method | Purpose |
|---|---|
loadBanner(options) |
Convenience alias for banner display |
showBanner(options) |
Show anchored banner |
hideBanner(placementId) |
Hide banner container without destroying placement identity |
destroyBanner(placementId) |
Fully destroy banner instance and container |
| Method | Purpose |
|---|---|
preloadInterstitial(options) |
Preload interstitial inventory |
isInterstitialReady(placementId) |
Check whether an interstitial is ready |
showInterstitial(placementId) |
Show a ready interstitial |
| Method | Purpose |
|---|---|
preloadRewarded(options) |
Preload rewarded inventory |
isRewardedReady(placementId) |
Check whether a rewarded ad is ready |
showRewarded(placementId) |
Show a ready rewarded ad |
| Method | Purpose |
|---|---|
preloadRewardedInterstitial(options) |
Preload rewarded interstitial inventory |
isRewardedInterstitialReady(placementId) |
Check whether a rewarded interstitial is ready |
showRewardedInterstitial(placementId) |
Show a ready rewarded interstitial |
| Method | Purpose |
|---|---|
preloadAppOpen(options) |
Preload app open inventory |
isAppOpenReady(placementId) |
Check whether an app open ad is ready |
showAppOpen(placementId) |
Show a ready app open ad |
| Method | Purpose |
|---|---|
preloadNative(options) |
Preload a native slot |
isNativeReady(slotId) |
Check whether a native slot is ready |
attachNative(options) |
Attach a native slot to a UI host |
detachNative(slotId) |
Detach native view from host while keeping slot lifecycle |
destroyNative(slotId) |
Destroy native slot state |
refreshNative(options) |
Force a fresh native preload cycle |
| Method | Purpose |
|---|---|
preloadInlineBanner(options) |
Preload an inline banner slot |
isInlineBannerReady(slotId) |
Check whether an inline banner slot is ready |
attachInlineBanner(options) |
Attach an inline banner slot to a UI host |
detachInlineBanner(slotId) |
Detach inline banner from host while keeping slot lifecycle |
destroyInlineBanner(slotId) |
Destroy inline banner slot state |
refreshInlineBanner(options) |
Force a fresh inline banner preload cycle |
| Method | Purpose |
|---|---|
addListener("adEvent", listener) |
Subscribe to cross-format lifecycle events |
addListener("adLog", listener) |
Subscribe to structured diagnostics and runtime debug logs |
removeAllListeners() |
Remove all registered listeners |
All plugin methods return a Promise<BridgeResult<...>>.
| Method family | Typical status values |
data payload |
|---|---|---|
configure(), configureRequest(), clearAll() |
ready, disabled |
usually omitted |
requestConsentInfo(), showConsentFormIfRequired(), showPrivacyOptions(), getConsentStatus() |
ready, not_ready |
ConsentInfo |
getTrackingAuthorizationStatus(), requestTrackingAuthorization() |
ready, unsupported |
{ status } |
preloadInterstitial(), preloadRewarded(), preloadRewardedInterstitial(), preloadAppOpen(), preloadNative(), preloadInlineBanner() |
loading, ready, disabled, error |
usually omitted |
showBanner(), hideBanner(), destroyBanner() |
loading, ready, disabled, error |
usually omitted |
showInterstitial(), showRewarded(), showRewardedInterstitial(), showAppOpen() |
ready, not_ready, error |
usually omitted |
attachNative(), detachNative(), destroyNative(), refreshNative() |
ready, loading, not_ready, error |
usually omitted |
attachInlineBanner(), detachInlineBanner(), destroyInlineBanner(), refreshInlineBanner() |
ready, loading, not_ready, error |
usually omitted |
isInterstitialReady(), isRewardedReady(), isRewardedInterstitialReady(), isAppOpenReady(), isNativeReady(), isInlineBannerReady() |
ready, not_ready |
{ ready: boolean } |
getRuntimeInfo() |
ready, disabled |
RuntimeInfo |
Simple success:
{
ok: true,
status: "ready",
}Loading result:
{
ok: true,
status: "loading",
}Readiness result:
{
ok: true,
status: "ready",
data: {
ready: true,
},
}Failure result:
{
ok: false,
code: "CONFIG_MISSING",
message: "placementId is required.",
status: "error",
}npm install @donugr/admob
npx cap syncPeer dependency:
@capacitor/core@^8
Add your AdMob application ID to your app AndroidManifest.xml:
<meta-data
android:name="com.google.android.gms.ads.APPLICATION_ID"
android:value="ca-app-pub-xxxxxxxxxxxxxxxx~yyyyyyyyyy" />You can also pass applicationId through configure(), but manifest-based setup is the safer default for public plugin consumers.
import { DonugrAdmob } from "@donugr/admob"
await DonugrAdmob.configure({
enabled: true,
testMode: true,
releaseSystemUiOnAdInteraction: true,
loggingLevel: "debug",
emitAdEvents: false,
placements: {
banner_home: "ca-app-pub-xxxx/banner",
interstitial_break: "ca-app-pub-xxxx/interstitial",
rewarded_bonus: "ca-app-pub-xxxx/rewarded",
app_open_launch: "ca-app-pub-xxxx/appopen",
native_feed_card: "ca-app-pub-xxxx/native",
inline_feed_banner: "ca-app-pub-xxxx/inline",
},
})Production-oriented placement example:
await DonugrAdmob.configure({
enabled: true,
testMode: false,
releaseSystemUiOnAdInteraction: true,
placements: {
banner_home: "ca-app-pub-xxxx/banner-home",
interstitial_break: "ca-app-pub-xxxx/interstitial-break",
rewarded_bonus: "ca-app-pub-xxxx/rewarded-bonus",
rewarded_interstitial_bonus: "ca-app-pub-xxxx/rewarded-interstitial-bonus",
app_open_launch: "ca-app-pub-xxxx/app-open-launch",
native_feed_card: "ca-app-pub-xxxx/native-feed-card",
inline_feed_banner: "ca-app-pub-xxxx/inline-feed-banner",
},
})Consumer recommendation:
- prefer placement mapping in
configure()as the default production path - use
adUnitIdonly when you intentionally need an explicit per-call override - avoid mixing production
adUnitIdoverrides withtestMode: true
Runtime diagnostics defaults:
loggingLeveldefaults to"off"emitAdEventsdefaults tofalse- enable
loggingLevel: "debug"during integration or layout troubleshooting - keep
emitAdEvents: falseunless the app explicitly needs lifecycle telemetry in JS
System UI safety note:
releaseSystemUiOnAdInteractiondefaults totrue- when enabled, the plugin asks Android to show system bars again before fullscreen ads show and when ad clicks occur
- this helps app consumers that normally run immersive/fullscreen shells, especially if hardware back or navigation bars are heavily customized
- this setting does not change the ad click destination; it only helps restore user escape affordances such as visible system bars
Keep testMode: true and use Google test ads or test devices during development.
This release keeps the public method names stable, but there are a few runtime-behavior notes for existing consumers:
adLogis new and is controlled byloggingLeveladEventshould now be treated as opt-in runtime telemetry and is controlled byemitAdEvents- if an older consumer previously relied on receiving
adEventwithout explicit runtime config, setemitAdEvents: trueinconfigure(...) - if an older consumer previously relied on
adb logcatonly for troubleshooting,adLogcan now be consumed directly in JS without changing the main ad methods - host-based methods remain backward-compatible in signature, but Android runtime logs are now more explicit about stale, disposed, duplicate, and geometry-related branches
await DonugrAdmob.configureRequest({
maxAdContentRating: "T",
tagForChildDirectedTreatment: false,
tagForUnderAgeOfConsent: false,
testDeviceIds: ["YOUR_TEST_DEVICE_ID"],
appMuted: false,
appVolume: 1,
})configureRequest() lets the app consumer define global ad-request behavior before loading ads.
All fields in configureRequest() are optional.
Default behavior:
maxAdContentRating:""tagForChildDirectedTreatment:nulltagForUnderAgeOfConsent:nulltestDeviceIds: omittedappMuted: omittedappVolume: omitted
Default interpretation:
""means no explicit content-rating cap from the pluginnullmeans the plugin does not force that privacy flag in the request configuration- omitted audio and test-device fields mean the plugin does not override them
Controls the maximum ad content rating that Google should return for requests made by the app.
Allowed values:
"G": general audiences"PG": parental guidance"T": teen audiences"MA": mature audiences"": no explicit content-rating cap
Practical guidance:
- use
"G"for child-oriented or highly conservative apps - use
"PG"for general consumer apps that still want a conservative filter - use
"T"for broader mainstream apps - use
"MA"only when the app experience and audience justify it - use
""only if the consumer intentionally wants no explicit cap from this setting
Signals whether requests should be treated as child-directed.
Values:
true: request child-directed treatmentfalse: explicitly indicate the request is not child-directednullor omit the field: do not override this setting in the request configuration
Important note:
- this is a policy-sensitive flag
- app consumers should only set it when their legal and product requirements actually call for child-directed treatment
Signals whether the user should be treated as under the age of consent for ad-request handling.
Values:
true: mark requests as under age of consentfalse: explicitly indicate they are not under age of consentnullor omit the field: do not override this setting in the request configuration
Important note:
- this should be driven by the app consumer's compliance logic, not guessed from UI state alone
Defines specific devices that should receive test ads when the consumer uses their own ad unit IDs.
Use this when:
- validating production-like placements in development or staging
- checking real placement mapping without risking invalid live-ad interaction
Android emulators are typically treated as test devices automatically, but physical devices should still be configured explicitly when needed.
Controls whether ad audio should start muted at the SDK level.
Values:
true: mute ad audiofalse: allow ad audio according to the ad creative and platform behavior
Practical guidance:
trueis often safer for apps that want a quieter default experiencefalsemay be acceptable if the app experience already expects audible media
Sets the global app volume hint for the Mobile Ads SDK.
Expected range:
0= silent1= full volume
The plugin clamps the value into the valid 0 to 1 range.
Practical guidance:
0for fully muted behavior0.5for reduced volume1for full volume
This configuration:
await DonugrAdmob.configureRequest({
maxAdContentRating: "T",
tagForChildDirectedTreatment: false,
tagForUnderAgeOfConsent: false,
testDeviceIds: ["YOUR_TEST_DEVICE_ID"],
appMuted: false,
appVolume: 1,
})means:
- request ads up to teen-rated content
- do not mark requests as child-directed
- do not mark requests as under age of consent
- serve test ads on the listed device when using consumer-owned ad unit IDs
- allow ad audio
- set app audio volume to full scale for the Mobile Ads SDK
For development convenience, supported load methods may accept a testAdPreset.
Supported presets:
app_openbanner_fixedbanner_adaptivebanner_inline_adaptiveinterstitialrewardedrewarded_interstitialnativenative_video
Rules:
testAdPresetis development-onlytestAdPresetis only valid whentestMode: true- when
testMode: false,testAdPresetis rejected explicitly - preset names are format-specific and cannot be mixed across methods
Resolution order:
adUnitIdtestAdPresetplacements[placementId]
Important behavior:
- if
testMode: trueand the consumer still passes an explicitadUnitId, that explicitadUnitIdwins - this means a production ad unit ID will still be used if it is passed explicitly
- for safe development behavior, do not send production
adUnitIdvalues whentestMode: true; prefertestAdPreset, test placements, and Google test device configuration
Compatibility note:
- to avoid breaking existing integrations, Android keeps the legacy test-ad fallback when
testMode: trueand none ofadUnitId,testAdPreset, orplacements[placementId]resolves an ad unit
Example:
await DonugrAdmob.preloadInlineBanner({
placementId: "inline_feed_banner",
slotId: "feed.banner.1",
hostId: "feed.banner.host.1",
testAdPreset: "banner_inline_adaptive",
})Additional examples:
await DonugrAdmob.showBanner({
placementId: "banner_home",
testAdPreset: "banner_adaptive",
})
await DonugrAdmob.preloadInterstitial({
placementId: "interstitial_break",
testAdPreset: "interstitial",
})
await DonugrAdmob.preloadRewarded({
placementId: "rewarded_bonus",
testAdPreset: "rewarded",
})
await DonugrAdmob.preloadRewardedInterstitial({
placementId: "rewarded_interstitial_bonus",
testAdPreset: "rewarded_interstitial",
})
await DonugrAdmob.preloadAppOpen({
placementId: "app_open_launch",
testAdPreset: "app_open",
})
await DonugrAdmob.preloadNative({
placementId: "native_feed_card",
slotId: "feed.slot.1",
hostId: "feed.card.1",
testAdPreset: "native_video",
mediaMode: "video_preferred",
})Preset-to-method guidance:
| Method | Supported testAdPreset |
|---|---|
showBanner() / loadBanner() |
banner_fixed, banner_adaptive |
preloadInterstitial() |
interstitial |
preloadRewarded() |
rewarded |
preloadRewardedInterstitial() |
rewarded_interstitial |
preloadAppOpen() |
app_open |
preloadNative() |
native, native_video |
preloadInlineBanner() |
banner_inline_adaptive |
Android consent is powered by Google User Messaging Platform.
const consentInfo = await DonugrAdmob.requestConsentInfo()
if (consentInfo.data?.canRequestAds === false) {
await DonugrAdmob.showConsentFormIfRequired()
}Available consent methods:
requestConsentInfo()showConsentFormIfRequired()showPrivacyOptions()getConsentStatus()resetConsentForTesting()
Supported options:
| Field | Type | Required | Notes |
|---|---|---|---|
placementId |
string |
Yes | Placement key used to resolve inventory |
adUnitId |
string |
No | Explicit override ad unit ID |
testAdPreset |
"banner_fixed" | "banner_adaptive" |
No | Development-only preset when testMode: true |
position |
"top" | "bottom" |
No | Defaults to "bottom" in current Android behavior |
await DonugrAdmob.showBanner({
placementId: "banner_home",
position: "bottom",
})Current Android banner behavior is intentionally conservative and oriented to anchored adaptive usage.
Supported preload options:
| Field | Type | Required | Notes |
|---|---|---|---|
placementId |
string |
Yes | Placement key used to resolve inventory |
adUnitId |
string |
No | Explicit override ad unit ID |
testAdPreset |
"interstitial" |
No | Development-only preset when testMode: true |
await DonugrAdmob.preloadInterstitial({
placementId: "interstitial_break",
})
const ready = await DonugrAdmob.isInterstitialReady("interstitial_break")
if (ready.data?.ready) {
await DonugrAdmob.showInterstitial("interstitial_break")
}Use interstitials only at natural transitions.
Supported preload options:
| Field | Type | Required | Notes |
|---|---|---|---|
placementId |
string |
Yes | Placement key used to resolve inventory |
adUnitId |
string |
No | Explicit override ad unit ID |
testAdPreset |
"rewarded" |
No | Development-only preset when testMode: true |
await DonugrAdmob.preloadRewarded({
placementId: "rewarded_bonus",
})
const ready = await DonugrAdmob.isRewardedReady("rewarded_bonus")
if (ready.data?.ready) {
await DonugrAdmob.showRewarded("rewarded_bonus")
}Only grant rewards from the reward-earned path in your app logic.
When to choose Rewarded vs Rewarded Interstitial:
- choose
Rewardedif your app already treats the ad as an explicit user action such as "watch to unlock" - choose
Rewarded Interstitialif you want the rewarded outcome but prefer Google rewarded-interstitial inventory and presentation - in both cases, only grant the reward from the
reward_earnedevent path
Supported preload options:
| Field | Type | Required | Notes |
|---|---|---|---|
placementId |
string |
Yes | Placement key used to resolve inventory |
adUnitId |
string |
No | Explicit override ad unit ID |
testAdPreset |
"rewarded_interstitial" |
No | Development-only preset when testMode: true |
await DonugrAdmob.preloadRewardedInterstitial({
placementId: "rewarded_interstitial_bonus",
})
const ready = await DonugrAdmob.isRewardedInterstitialReady("rewarded_interstitial_bonus")
if (ready.data?.ready) {
await DonugrAdmob.showRewardedInterstitial("rewarded_interstitial_bonus")
}Use rewarded interstitial only where the reward exchange is explicit to the user, just like standard rewarded flow.
Supported preload options:
| Field | Type | Required | Notes |
|---|---|---|---|
placementId |
string |
Yes | Placement key used to resolve inventory |
adUnitId |
string |
No | Explicit override ad unit ID |
testAdPreset |
"app_open" |
No | Development-only preset when testMode: true |
await DonugrAdmob.preloadAppOpen({
placementId: "app_open_launch",
})
const ready = await DonugrAdmob.isAppOpenReady("app_open_launch")
if (ready.data?.ready) {
await DonugrAdmob.showAppOpen("app_open_launch")
}Use app open ads for launch, foreground return, or explicit loading moments, not arbitrary content interruption.
Native placements use three identifiers:
placementId: stable inventory identityslotId: runtime ad-slot lifecycle identityhostId: logical UI host identity
Supported options:
| Field | Type | Required | Notes |
|---|---|---|---|
placementId |
string |
Yes | Placement key used to resolve inventory |
slotId |
string |
Yes | Runtime slot identity |
hostId |
string |
Yes | Logical host identity |
adUnitId |
string |
No | Explicit override ad unit ID |
testAdPreset |
"native" | "native_video" |
No | Development-only preset when testMode: true |
mediaMode |
"auto" | "video_preferred" |
No | video_preferred asks for video-capable media when available, but image fallback remains valid |
ttlMs |
number |
No | Native slot lifetime before stale cleanup |
hostRect.x |
number |
No | Host left coordinate in px |
hostRect.y |
number |
No | Host top coordinate in px |
hostRect.width |
number |
No | Host width in px |
hostRect.height |
number |
No | Host height in px |
hostRect.anchor |
"top" | "bottom" |
No | Anchor hint for host layout |
adSizeStrategy |
"current_orientation" | "landscape" | "portrait" |
No | Android inline adaptive sizing strategy used when hostRect.height is not set |
Inline banner sizing note:
adSizeStrategydefaults to"current_orientation"- if
hostRect.heightis provided and greater than zero, Android keeps using the max-height inline adaptive request path and ignoresadSizeStrategy "landscape"and"portrait"influence the size request strategy only; they do not force the creative itself to become a specific orientation
await DonugrAdmob.preloadNative({
placementId: "native_feed_card",
slotId: "feed.slot.1",
hostId: "feed.card.1",
mediaMode: "video_preferred",
ttlMs: 60000,
})
const ready = await DonugrAdmob.isNativeReady("feed.slot.1")
if (ready.data?.ready) {
await DonugrAdmob.attachNative({
placementId: "native_feed_card",
slotId: "feed.slot.1",
hostId: "feed.card.1",
hostRect: {
x: 16,
y: 320,
width: 360,
height: 120,
anchor: "top",
},
})
}Native video note:
- native video is exposed through the native host engine, not as a separate top-level format
- set
mediaMode: "video_preferred"when the placement should prefer video-capable creatives - even with
mediaMode: "video_preferred", Google inventory may still return an image-native creative, so the consumer must treat image fallback as valid behavior
Recommended consumer pattern:
- use one placement identity for the feed surface, for example
native_feed_card - use
mediaMode: "auto"for general native slots - use
mediaMode: "video_preferred"only on surfaces where video-native creatives make UX sense - design the host card so both video and image-native creatives look acceptable without layout breakage
Available native methods:
preloadNative()isNativeReady()attachNative()detachNative()destroyNative()refreshNative()
Built-in protections:
- duplicate attach guard
- same-host same-rect layout skip
- TTL cleanup for stale slots
- cleanup on plugin destroy
- identifier validation for
placementId,slotId, andhostId
Inline banner is separate from anchored banner. It is intended for scrolling content placements, not global sticky takeovers.
Coordinate note for WebView-based apps:
hostRectis expected to come from the WebView viewport, such asgetBoundingClientRect()- prefer using the exported
buildNativeHostRect(element)helper so the same rounding rules are reused consistently - Android inline banner placement is still a native overlay, not true inline DOM rendering
- the plugin normalizes
hostRectrelative to the Capacitor WebView before placing the native overlay - the Android plugin also applies a best-effort CSS-pixel to native-pixel normalization heuristic, because DOM viewport rects and native overlay coordinates do not always share the same scale
- if the WebView layout changes because of scroll, resize, async content, keyboard, or orientation changes, the consumer should call
attachInlineBanner()again with the latest rect - the plugin reuses the loaded ad view for relayout and skips tiny layout jitter where possible, but it does not automatically track DOM movement on every frame
Recommended re-attach moments:
- after the host element first becomes visible in the DOM
- after list virtualization or infinite-scroll inserts content above the host
- after route transitions or tab switches that relayout the WebView
- after orientation changes
- after keyboard open or close if the page shifts vertically
- after image or async content loads that change the final host position
Scale and viewport note:
buildNativeHostRect(element)usesgetBoundingClientRect()and rounds to integer viewport pixels- this is the safest default for standard Capacitor WebView layouts and should be preferred over hand-built rect math
- during attach, Android evaluates multiple normalization candidates, including WebView-relative and density-scaled interpretations, before choosing the applied overlay rect
- if a consumer applies custom zoom, non-standard viewport scaling, or transforms that visually move the host without changing normal layout flow, overlay alignment can still drift because Android is rendering a native overlay, not DOM content
- when diagnosing a mismatch, compare the DOM host rect,
window.innerWidth, and the inline banner debug messages emitted by the plugin to determine whether the issue is offset-related or scale-related
Supported options:
| Field | Type | Required | Notes |
|---|---|---|---|
placementId |
string |
Yes | Placement key used to resolve inventory |
slotId |
string |
Yes | Runtime slot identity |
hostId |
string |
Yes | Logical host identity |
adUnitId |
string |
No | Explicit override ad unit ID |
testAdPreset |
"banner_inline_adaptive" |
No | Development-only preset when testMode: true |
hostRect.x |
number |
No | Host left coordinate in px |
hostRect.y |
number |
No | Host top coordinate in px |
hostRect.width |
number |
No | Host width in px |
hostRect.height |
number |
No | Host height in px |
hostRect.anchor |
"top" | "bottom" |
No | Anchor hint for host layout |
await DonugrAdmob.preloadInlineBanner({
placementId: "inline_feed_banner",
slotId: "feed.banner.1",
hostId: "feed.banner.host.1",
adSizeStrategy: "landscape",
hostRect: {
x: 16,
y: 640,
width: 360,
anchor: "top",
},
})
const ready = await DonugrAdmob.isInlineBannerReady("feed.banner.1")
if (ready.data?.ready) {
await DonugrAdmob.attachInlineBanner({
placementId: "inline_feed_banner",
slotId: "feed.banner.1",
hostId: "feed.banner.host.1",
adSizeStrategy: "landscape",
hostRect: {
x: 16,
y: 640,
width: 360,
anchor: "top",
},
})
}If you want Android to honor a specific maximum inline banner height instead, pass hostRect.height and omit adSizeStrategy.
Available inline banner methods:
preloadInlineBanner()isInlineBannerReady()attachInlineBanner()detachInlineBanner()destroyInlineBanner()refreshInlineBanner()
Minimal re-attach example with light throttling:
import { DonugrAdmob, buildNativeHostRect } from "@donugr/admob"
const hostElement = document.querySelector("[data-inline-banner-host]")
let reattachTimer: ReturnType<typeof setTimeout> | null = null
async function reattachInlineBanner() {
if (!hostElement) return
await DonugrAdmob.attachInlineBanner({
placementId: "inline_feed_banner",
slotId: "feed.banner.1",
hostId: "feed.banner.host.1",
hostRect: buildNativeHostRect(hostElement),
})
}
function scheduleInlineBannerRelayout() {
if (reattachTimer) {
clearTimeout(reattachTimer)
}
reattachTimer = setTimeout(() => {
void reattachInlineBanner()
}, 80)
}
window.addEventListener("resize", scheduleInlineBannerRelayout, { passive: true })
window.addEventListener("orientationchange", scheduleInlineBannerRelayout, { passive: true })
document.addEventListener("scroll", scheduleInlineBannerRelayout, { passive: true })Built-in protections:
- host-based
slotIdlifecycle - duplicate attach guard
- same-host same-rect layout skip
- isolated overlay namespace from native containers
- identifier validation for
placementId,slotId, andhostId
Detach vs destroy guidance:
detachInlineBanner(slotId)removes the native view from its host but keeps the slot lifecycle when the loaded ad is still reusabledestroyInlineBanner(slotId)tears down the slot and should be used when the placement truly leaves the page or routedetachNative(slotId)anddestroyNative(slotId)follow the same rule for native host placements
Route-change cleanup guidance:
- use
detach...()for temporary host loss such as virtualization reuse, tab swaps, or short relayout windows - use
destroy...()when the page, route, or logical placement is finished and should not be reused - on route leave, destroy host-based slots that will not immediately reattach on the next view
Listen to the adEvent channel for shared lifecycle telemetry:
const handle = await DonugrAdmob.addListener("adEvent", event => {
console.log(event.format, event.placementId, event.slotId, event.phase)
})Common event phases:
loadedfailedshowndismissedclickedimpressionreward_earnedattacheddetacheddestroyedconsent_updated
Additional preload and layout phases are emitted for native and inline banner diagnostics.
Use adLog for structured diagnostics from the plugin:
const logHandle = await DonugrAdmob.addListener("adLog", log => {
console.log("[admob]", log.level, log.scope, log.code, log.message, log.data)
})Logging behavior:
"off": plugin emits noadLog"error": only error diagnostics"warn": error and warning diagnostics"info": error, warning, and info diagnostics"debug": full diagnostics including geometry, callback-order, and state-transition traces
Use adEvent and adLog differently:
adEventis lifecycle-facing telemetry for app logicadLogis diagnostics-facing telemetry for debugging and QAemitAdEventscontrolsadEventloggingLevelcontrolsadLog
Event payload shape:
| Field | Type | Always present | Notes |
|---|---|---|---|
format |
"banner" | "interstitial" | "rewarded" | "native" | "inline_banner" | "app_open" |
Yes | Ad format that emitted the event |
placementId |
string |
Yes | Placement identity |
slotId |
string |
No | Present for host-based formats such as native and inline banner |
phase |
string |
Yes | Lifecycle phase |
code |
string |
No | Error or diagnostic code when relevant |
message |
string |
No | Human-readable event message |
Example event payload:
{
format: "native",
placementId: "native_feed_card",
slotId: "feed.slot.1",
phase: "loaded",
message: "Native ad loaded.",
}Common phase usage:
loaded: ad finished loading successfullyfailed: ad load or show failedshown: fullscreen or banner open eventdismissed: fullscreen ad closedclicked: ad click recordedimpression: impression recordedreward_earned: rewarded callback pathattached: host-based ad attached to a UI hostdetached: host-based ad detached from a UI hostdestroyed: slot or banner destroyedconsent_updated: consent state changedpreload_start,preload_reused,preload_skip_loading,attach_skipped_same_host,layout_skipped_same_rect: diagnostics for native and inline host flows
Diagnostics payload shape:
| Field | Type | Always present | Notes |
|---|---|---|---|
level |
"error" | "warn" | "info" | "debug" |
Yes | Log severity after loggingLevel filtering |
scope |
"core" | "consent" | "banner" | "interstitial" | "rewarded" | "rewarded_interstitial" | "app_open" | "native" | "inline_banner" |
Yes | Runtime area that emitted the log |
code |
string |
Yes | Stable log code for grouping and troubleshooting |
message |
string |
Yes | Human-readable diagnostic message |
placementId |
string |
No | Present when the log relates to a placement |
slotId |
string |
No | Present for host-based logs |
hostId |
string |
No | Present for host-based logs |
phase |
string |
No | Optional lifecycle phase context |
data |
object |
No | Structured diagnostics such as geometry, layout params, or callback context |
timestamp |
number |
Yes | Epoch milliseconds when the log was emitted |
All plugin methods resolve to a BridgeResult<T> shape.
Base result shape:
| Field | Type | Always present | Notes |
|---|---|---|---|
ok |
boolean |
Yes | true for success, false for failure |
status |
"ready" | "loading" | "not_ready" | "disabled" | "unsupported" | "error" |
No | High-level availability state |
code |
string |
No | Failure code when ok is false |
message |
string |
No | Failure or diagnostic message |
data |
object |
No | Method-specific payload |
Success example:
{
ok: true,
status: "ready",
data: {
ready: true,
},
}Failure example:
{
ok: false,
code: "NOT_READY",
message: "Interstitial is not ready.",
status: "not_ready",
}Methods:
isInterstitialReady()isRewardedReady()isAppOpenReady()isNativeReady()isInlineBannerReady()
Payload shape:
| Field | Type | Notes |
|---|---|---|
ready |
boolean |
Indicates whether the ad or slot is currently ready |
Example:
const result = await DonugrAdmob.isInterstitialReady("interstitial_break")
if (result.ok && result.data?.ready) {
await DonugrAdmob.showInterstitial("interstitial_break")
}Methods:
requestConsentInfo()showConsentFormIfRequired()showPrivacyOptions()getConsentStatus()
Payload shape:
| Field | Type | Notes |
|---|---|---|
status |
"unknown" | "required" | "not_required" | "obtained" | "denied" |
Consent status surface exposed by the plugin |
canRequestAds |
boolean |
Whether ads can be requested at this point |
privacyOptionsRequired |
boolean |
Whether privacy options should be shown |
Example:
const consent = await DonugrAdmob.requestConsentInfo()
if (consent.ok && consent.data?.canRequestAds) {
// safe to continue loading ads
}Methods:
getTrackingAuthorizationStatus()requestTrackingAuthorization()
Payload shape:
| Field | Type | Notes |
|---|---|---|
status |
"not_determined" | "restricted" | "denied" | "authorized" | "unsupported" |
Tracking authorization status |
Method:
getRuntimeInfo()
Payload shape:
| Field | Type | Notes |
|---|---|---|
platform |
"android" | "ios" | "web" |
Current platform |
enabled |
boolean |
Whether ads are enabled in plugin runtime config |
testMode |
boolean |
Whether runtime is in test mode |
releaseSystemUiOnAdInteraction |
boolean |
Whether Android should restore system bars during ad interaction safety handling |
applicationIdConfigured |
boolean |
Whether an app ID is configured |
applicationIdSource |
"js" | "android_manifest" | "ios_plist" | "missing" |
Where the app ID came from |
placementsConfigured |
number |
Number of registered placements |
requestConfigurationConfigured |
boolean |
Whether request configuration has been applied |
consentStatus |
string |
Current consent status |
activeSlots |
number |
Native slot count when available |
loadingSlots |
number |
Native loading slot count when available |
readySlots |
number |
Native ready slot count when available |
attachedSlots |
number |
Native attached slot count when available |
failedSlots |
number |
Native failed slot count when available |
expiredSlots |
number |
Native expired slot count when available |
inlineBannerActiveSlots |
number |
Inline banner slot count when available |
inlineBannerLoadingSlots |
number |
Inline banner loading count when available |
inlineBannerReadySlots |
number |
Inline banner ready count when available |
inlineBannerAttachedSlots |
number |
Inline banner attached count when available |
inlineBannerFailedSlots |
number |
Inline banner failed count when available |
Example:
const runtime = await DonugrAdmob.getRuntimeInfo()
if (runtime.ok) {
console.log(runtime.data?.platform, runtime.data?.testMode)
}const runtime = await DonugrAdmob.getRuntimeInfo()Current Android runtime info includes:
enabledtestModereleaseSystemUiOnAdInteractionloggingLevelemitAdEventsapplicationIdConfiguredapplicationIdSourceplacementsConfiguredrequestConfigurationConfiguredconsentStatusactiveSlotsloadingSlotsreadySlotsattachedSlotsfailedSlotsexpiredSlotsinlineBannerActiveSlotsinlineBannerLoadingSlotsinlineBannerReadySlotsinlineBannerAttachedSlotsinlineBannerFailedSlots
This plugin does not attempt to bypass, soften, or reinterpret Google AdMob policy.
- always use test ads during development
- for reward-based formats, make the reward contract explicit in the app UI before show
- do not label a native placement as guaranteed video unless your app also handles image fallback correctly
- if your app uses immersive mode or disables back/navigation aggressively, keep a tested recovery path so users can still return after ad interaction
- do not inflate clicks or impressions
- do not place interstitials at disruptive moments
- do not grant rewarded outcomes before reward completion
- do not use app open ads as a generic fullscreen interruption tool
- do not use inline banner host mode as a disguised sticky or takeover ad
Plugin consumers remain responsible for compliant ad placement, disclosure, and app behavior.
Use development mode when wiring the plugin, validating layout, and checking event flow.
- set
testMode: true - prefer
loggingLevel: "debug"while integrating - keep
emitAdEventsdisabled unless the app is actively consuming them - you may use
testAdPreset - or use your own ad units plus
testDeviceIds - do not click live ads without proper test-device setup
Use staging to verify real placement mapping before release.
- prefer your own app ad unit IDs
- keep
testDeviceIdsenabled where appropriate - reduce dependence on
testAdPreset - prefer
loggingLevel: "info"or"warn"unless geometry troubleshooting is still active - validate consent flow, fullscreen timing, and host-based layout behavior
Use production inventory owned by the consumer app.
- set
testMode: false - keep
loggingLevel: "off"unless short-term field diagnostics are needed - keep
emitAdEvents: falseby default unless the app intentionally maps lifecycle telemetry - do not use
testAdPreset - rely on
placementsas the default inventory mapping - use
adUnitIdonly as an explicit override when truly needed - keep ad unit formats aligned with plugin methods
Production format guidance:
showBanner()uses Banner ad unitspreloadInlineBanner()uses Banner ad unitspreloadNative()uses Native ad unitspreloadInterstitial()uses Interstitial ad unitspreloadRewarded()uses Rewarded ad unitspreloadRewardedInterstitial()uses Rewarded Interstitial ad unitspreloadAppOpen()uses App Open ad units
This package is intended for public npm usage, so the public API aims to stay:
- Android-first and explicit about current platform support
- conservative around policy-sensitive behavior
- stable in naming for
placementId,slotId,hostId, and event phases - debuggable through shared runtime and event contracts
All Android ad formats use real Google Mobile Ads SDK implementations. Production release still requires integration testing with Google test ads, consent states, physical devices, background/foreground transitions, orientation changes, no-fill handling, and duplicate-call protection.
| Format | creativeSize |
hostSize |
fullscreen |
reward |
|---|---|---|---|---|
| Banner | Yes | No | No | No |
| Inline banner | Yes | Yes | No | No |
| Native | No | Yes | No | No |
| Interstitial | No | No | Yes | No |
| Rewarded | No | No | Yes | Yes |
| Rewarded interstitial | No | No | Yes | Yes |
| App open | No | No | Yes | No |
creativeSize is the Google AdView size in dp and physical pixels. hostSize is the application host rectangle and must not be interpreted as an intrinsic native creative size. Fullscreen formats intentionally do not report a creative size.
Recommended flow: preload, check readiness, show, wait for dismissed or failed, then preload the next instance. A global fullscreen lock prevents two fullscreen formats from being shown at the same time. A second show request returns FULLSCREEN_ALREADY_SHOWING.
Grant rewards only from phase: "reward_earned" and read data.reward.amount plus data.reward.type. Do not grant rewards from shown, impression, or dismissed. Applications should use an idempotency guard so a repeated event cannot grant the same reward twice.
App open ads include an internal freshness check. The application remains responsible for deciding when to show them. Do not show while consent UI, critical startup UI, payments, or another fullscreen ad is active. On foreground: resolve consent, confirm readiness and freshness, confirm no fullscreen ad is active, then show.
Use adEvent for application behavior and business logic. Use adLog only for diagnostics such as state transitions, stale callbacks, geometry, duplicate requests, and cleanup. Business logic must not depend on adLog.
- Use Google test ad units during development.
- Test consent accepted, denied, and not-required states.
- Test preload, readiness, show, dismissal, failure, and no-fill.
- Test duplicate preload and duplicate show calls.
- Test background/foreground and orientation changes.
- Verify rewards are granted exactly once.
- Verify fullscreen concurrency protection.
- Verify app open freshness behavior.
- Test
clearAll()while requests are loading.