A beginner-friendly React Native example showing how to connect web ads inside
an Android WebView or iOS WKWebView to the Google Mobile Ads SDK.
The app contains a safe-area-aware launcher with three cards:
| Card | URL | Purpose |
|---|---|---|
| Google WebView Ads Test | https://google.github.io/webview-ads/test/ |
Validate WebView settings, GMA registration, media, cookies, and click behavior |
| Gamezop Portal | https://peslsv.play.gamezop.com |
Open the configured Gamezop games portal |
| Random Game | https://www.gamezop.com/g/random |
Resolve and launch an individual random game |
This is the React Native companion to:
gamezop/gma-android-webview-examplegamezop/gma-ios-webview-examplegamezop/gma-flutter-webview-example
Google provides official Google Mobile Ads SDKs for Android and iOS. Google
also documents MobileAds.registerWebView(...) on Android and
MobileAds.shared.register(...) on iOS.
Google does not currently provide an official React Native plugin or a JavaScript method that registers a React Native WebView.
That distinction matters because Google's API requires the real native object:
React Native JavaScript ref
↓ cannot be passed directly to GMA
android.webkit.WebView / WKWebView
↓
Google Mobile Ads registerWebView API
Calling a JavaScript initialization method is not enough. The exact native WebView that renders the monetized page must be registered.
This project solves that missing layer with a small custom native component:
- Kotlin creates and registers the Android
WebView. - Swift creates and registers the iOS
WKWebView. - TypeScript renders the component and handles shared UI state.
The native component is application code, not an official Google React Native integration. The Google Mobile Ads binaries underneath it are the official Google SDKs.
- Initializes the native Google Mobile Ads SDK.
- Creates an ad-optimized native WebView.
- Registers every WebView instance before its first URL load.
- Enables JavaScript, DOM storage, cookies, and inline media as appropriate.
- Keeps approved Gamezop and Google test pages embedded.
- Allows all subframe navigation, including
about:blankandabout:srcdoc. - Opens off-domain web click-outs without destroying the running game.
- Handles
target="_blank"and JavaScriptwindow.open(...). - Shows an iOS Simulator warning and a separate Android emulator warning.
- Supports React Native's New Architecture compatibility layer.
It does not display React Native banner, rewarded, or interstitial ads, and it does not include strict-mode or partner-security simulation.
- Android: Set up WebView
- Android: Integrate the WebView API for Ads
- Android: Optimize WebView click behavior
- iOS: Set up WKWebView
- iOS: Integrate the WebView API for Ads
- iOS: Optimize WKWebView click behavior
You need:
- Node.js 22.11 or newer
- npm
- React Native development prerequisites
- Android Studio, Android SDK, and a Java 17+ JDK
- Xcode 16+ and CocoaPods for iOS
Check the environment:
npx react-native doctorIf java -version reports Java 11 on macOS, use Android Studio's JDK for the
current terminal:
export JAVA_HOME="/Applications/Android Studio.app/Contents/jbr/Contents/Home"From the project root:
npm install
cd ios
pod install
cd ..Always open the generated .xcworkspace, not the .xcodeproj, when working in
Xcode:
open ios/GMAReactNativeWebViewExample.xcworkspaceKeep this command running in its own terminal:
npm startStart an Android emulator from Android Studio's Device Manager, or connect a physical device with USB debugging enabled.
Confirm that React Native can see it:
adb devicesRun the app:
npm run androidUse a Play Store-enabled emulator or a physical device when testing Google Play
redirects. An emulator without Play Store support might not have an activity
that understands market:// URLs.
Run the default simulator:
npm run iosChoose a specific simulator:
npx react-native run-ios --simulator "iPhone 17"App Store redirects cannot be fully tested in the iOS Simulator. Use a physical iPhone or iPad for that part of Google's click test.
The order is deliberate:
- React Native asks for a
GmaWebView. - Native code constructs the platform WebView.
- Native code applies ad-friendly settings.
- Native code initializes the Google Mobile Ads SDK once.
- Native code registers that exact WebView instance.
- Only after registration completes does native code load
sourceUrl. - Progress, error, and back-history events return to TypeScript.
Loading the URL before step 5 can cause the first page's supported ad tags to miss the native SDK connection.
App.tsx: tiny launcher/WebView routersrc/HomeScreen.tsx: safe-area launcher, cards, and platform warningssrc/GmaWebViewScreen.tsx: header, loading, errors, reload, and back handlingsrc/native/GmaWebView.tsx: typed native component declarationsrc/destinations.ts: the three editable URLssrc/navigationPolicy.ts: testable mirror of the native top-level allowlist
GmaWebView.kt: WebView settings, GMA registration, navigation, and click-outsGmaWebViewManager.kt: React properties and eventsGmaWebViewEvent.kt: Fabric-compatible event deliveryGmaWebViewPackage.kt: manual package registration
GmaWebViewManager.swift: WKWebView settings, one-time GMA startup, registration, navigation, and click-outsGmaWebViewManagerBridge.m: exposes Swift properties and events to React Native
The following section assumes a bare React Native Community CLI application. Expo Go cannot load custom Kotlin or Swift code. An Expo project must use a development build/prebuild workflow before following the native steps.
npm install react-native-safe-area-context react-native-device-inforeact-native-safe-area-context keeps the launcher and WebView clear of the
notch and system bars. react-native-device-info is only used to show the iOS
Simulator warning; omit it if that warning is unnecessary.
Copy these files into your app:
src/native/GmaWebView.tsx
src/GmaWebViewScreen.tsx
src/navigationPolicy.ts
Copy src/destinations.ts and src/HomeScreen.tsx if you also want this
sample's three-card launcher.
The important JavaScript declaration is:
export const GmaWebView =
requireNativeComponent<GmaWebViewProps>('GmaWebView');The string GmaWebView must exactly match the name exported by Android and iOS.
Render it with a network URL:
<GmaWebView
style={{flex: 1}}
sourceUrl="https://google.github.io/webview-ads/test/"
reloadToken={0}
goBackToken={0}
/>Do not replace the network URL with injected HTML when monetization matters. Google recommends loading network content directly so cookies and page URLs work as expected.
Open your app module's Gradle file, normally android/app/build.gradle, and add
these lines inside dependencies:
dependencies {
implementation("com.google.android.gms:play-services-ads:24.8.0")
implementation("androidx.browser:browser:1.9.0")
}This sample pins GMA 24.8.0 because React Native 0.86 uses a Kotlin 2.1
compiler. GMA 25.4.0 currently contains Kotlin 2.3 metadata and fails to
compile in that combination. Check compatibility before changing the version;
do not blindly replace it with the newest GMA release.
The browser dependency provides Android Custom Tabs for off-domain HTTPS click-outs.
Open android/app/src/main/AndroidManifest.xml.
Ensure internet permission exists directly under <manifest>:
<uses-permission android:name="android.permission.INTERNET" />Add the integration-manager entry inside <application>:
<meta-data
android:name="com.google.android.gms.ads.INTEGRATION_MANAGER"
android:value="webview" />This tells GMA that the SDK is used only for WebView APIs and bypasses the
normal native-ad application-ID check. Without this entry, GMA can throw an
IllegalStateException during startup.
If your app also displays native banner, interstitial, rewarded, or app-open ads, follow Google's quick start and configure the real GMA application ID instead of assuming this WebView-only bypass covers those formats.
Find your Android package directory:
android/app/src/main/java/com/yourcompany/yourapp/
Copy these four files into it:
GmaWebView.kt
GmaWebViewManager.kt
GmaWebViewEvent.kt
GmaWebViewPackage.kt
Change the first line of every copied file from:
package com.gamezop.gmareactnativewebviewexampleto your application's package, for example:
package com.yourcompany.yourappIf the package declarations do not match the directory/application package,
Android compilation will fail or GmaWebViewPackage will not resolve.
Open MainApplication.kt. In the PackageList(...).packages.apply block, add:
add(GmaWebViewPackage())It should resemble:
PackageList(this).packages.apply {
add(GmaWebViewPackage())
}This manual step is required because these Kotlin files belong to the app rather than an autolinked npm package.
The crucial code is inside GmaWebView.init:
MobileAds.initialize(context.applicationContext) {
post {
MobileAds.registerWebView(this)
isGmaReady = true
loadPendingSource()
}
}sourceUrl is stored while initialization is pending. loadPendingSource()
does nothing until isGmaReady becomes true. Keep that guard if you adapt the
component.
Open ios/Podfile and add the Google SDK inside your app target:
target 'YourApp' do
config = use_native_modules!
pod 'Google-Mobile-Ads-SDK', '13.7.0'
# existing React Native configuration...
endInstall it:
cd ios
pod install --repo-update
cd ..Use --repo-update when CocoaPods says it cannot find the requested SDK
version.
Open the application Info.plist and add:
<key>GADIntegrationManager</key>
<string>webview</string>This is the iOS equivalent of Android's WebView-only integration-manager
metadata. Without it, GMA can raise GADInvalidInitializationException when no
normal GMA application identifier is configured.
For production advertising, also copy Google's current SKAdNetworkItems list
into Info.plist from the
official iOS quick start.
Google updates that list over time, so this sample deliberately does not freeze
a potentially stale copy in source control. Missing entries do not prevent this
WebView example from launching, but they can reduce attribution coverage.
Copy:
GmaWebViewManager.swift
GmaWebViewManagerBridge.m
into your iOS application source directory.
Then add both files to Xcode:
- Open your
.xcworkspace. - Right-click the application group in the Project Navigator.
- Choose Add Files to "YourApp".
- Select both copied files.
- Ensure the application target is checked under Add to targets.
- Build once from Xcode.
Copying files into the folder without adding them to the Xcode target is not
enough. If the target membership is missing, JavaScript reports that
GmaWebView is unavailable.
The Objective-C .m bridge file is required even though the implementation is
Swift. It exports sourceUrl, command tokens, and events to React Native.
GmaWebViewRegistration starts the SDK once and queues each view:
MobileAds.shared.start { _ in
// On the main actor:
MobileAds.shared.register(webView)
completion()
}Only the completion sets isGmaReady and consumes the pending URL. Do not move
webView.load(...) ahead of registration.
Metro hot reload cannot install new native code. After adding Kotlin, Swift, Gradle, Pod, or plist changes, rebuild:
npm run androidand:
cd ios
pod install
cd ..
npm run ios- JavaScript enabled
- DOM storage enabled
- First-party cookies enabled
- Third-party cookies enabled
- Automatic media playback enabled
- Multiple windows enabled for
_blank/window.open - WebView debugging enabled in debug builds
- Persistent website storage
- Inline media playback enabled
- Automatic media playback enabled
- JavaScript window opening enabled
- Back/forward gestures enabled
Third-party cookies are not expected to pass Google's test on iOS due to platform behavior.
Top-level HTTPS navigation remains embedded for:
google.github.iogamezop.com- Any host ending in
.gamezop.com
The subdomain rule matters because /g/random can redirect to a game-specific
Gamezop host.
The allowlist is only applied to the top-level page. Subframes are allowed without host filtering because games, ads, analytics, and game engines may use:
- Additional HTTPS origins
about:blankabout:srcdocblob:URLsdata:URLs
Applying the top-level HTTPS allowlist to every iframe is a common reason games
stop during initialization with about:blank, about:srcdoc, or a
disallowedScheme error.
Off-domain HTTPS click-outs open using:
SFSafariViewControlleron iOS- Android Custom Tabs on Android
App Store, Play Store, and custom schemes are offered to the operating system. The original WebView stays alive so returning to the app preserves game state.
Open Google WebView Ads Test and check:
- JavaScript enabled
- First-party cookies work
- Third-party cookies work on Android
- Video plays inline
- Video starts automatically
- Video can replay
- WebView connected to the Google Mobile Ads SDK
- Google Publisher Tag connected to GMA
target="_top"href opens correctlytarget="_blank"href opens correctly- JavaScript
window.open("_blank")opens correctly - Returning from a click-out preserves the page counter/state
- Store links open the correct store application
The market:// test only applies to Android. App Store links require a physical
iOS device.
For network verification, inspect an ad request and confirm it includes the GMA app-signal parameter described by Google's WebView documentation.
- Rebuild the app; Metro reload is insufficient.
- Android: confirm
add(GmaWebViewPackage())exists. - Android: confirm all Kotlin package declarations match your app.
- iOS: confirm both native files belong to the application target.
- iOS: open the
.xcworkspace, not.xcodeproj.
Confirm this is inside <application>:
<meta-data
android:name="com.google.android.gms.ads.INTEGRATION_MANAGER"
android:value="webview" />Confirm Info.plist contains:
<key>GADIntegrationManager</key>
<string>webview</string>React Native 0.86 currently compiles with Kotlin 2.1 while GMA 25.4.0 publishes
Kotlin 2.3 metadata. Keep play-services-ads:24.8.0, or upgrade only after your
React Native/Kotlin toolchain supports the metadata version used by GMA.
On macOS:
export JAVA_HOME="/Applications/Android Studio.app/Contents/jbr/Contents/Home"
java -versionThen rerun the build from the same terminal.
- Confirm registration happens before
loadUrl/load. - Confirm the Google SDK dependency is present in the native target.
- Confirm the integration-manager metadata is present.
- Use the exact Google test URL before testing your own site.
- Verify the page uses GPT, AdSense, or IMA for HTML5; other ad tags do not use this API.
- Allow a few seconds for native SDK initialization on a fresh emulator. The sample intentionally keeps the loader visible instead of loading too early.
Add the current SKAdNetworkItems array from Google's
iOS quick start
before releasing a production app. The list changes independently of this
sample, so it is linked rather than duplicated here.
Android requires multiple-window support and WebChromeClient.onCreateWindow.
iOS requires both WKUIDelegate and WKNavigationDelegate. Copy the complete
native view implementation rather than only the registration line.
Do not apply a top-level scheme/host allowlist to iframe requests. This sample returns early for subframes and permits browser-internal document schemes.
Use an AVD image with the Play Store badge or test on a physical device. A plain
AOSP emulator may not include an application capable of opening market://.
That is an iOS Simulator limitation. Test store redirects on a physical device.
react-native-google-mobile-ads
is a community-maintained wrapper for native banner, interstitial, rewarded,
app-open, and native ad formats. It links the official native Google SDKs.
At the time of writing, it does not expose a JavaScript API that accepts and
registers the underlying android.webkit.WebView or WKWebView. Adding that
package and calling its initialization method does not by itself connect web ad
tags inside a React Native WebView.
You may use that package alongside this component if your app also displays native ad formats. Coordinate SDK versions and initialization in that case so the same native GMA SDK is not declared with conflicting versions.
npm run lint
npm test -- --runInBand
export JAVA_HOME="/Applications/Android Studio.app/Contents/jbr/Contents/Home"
cd android
./gradlew app:assembleDebug -PreactNativeArchitectures=arm64-v8a
cd ..
xcodebuild \
-workspace ios/GMAReactNativeWebViewExample.xcworkspace \
-scheme GMAReactNativeWebViewExample \
-sdk iphonesimulator \
-destination "platform=iOS Simulator,name=iPhone 17" \
-configuration Debug \
CODE_SIGNING_ALLOWED=NO \
buildThe Android debug APK is generated at:
android/app/build/outputs/apk/debug/app-debug.apk