Practice and reinforce the concepts from Lesson 15
Integrate Google AdMob to monetize your app through banner ads, interstitial ads between screens, and rewarded video ads that provide in-app benefits. Learn ad placement strategies, frequency capping, and user experience best practices.
A complete ad monetization system with banner ads at screen bottom, interstitials shown between actions, rewarded videos that grant coins/power-ups, and proper loading states with fallback handling.

Install AdMob library:
npm install react-native-google-mobile-ads
Configure AdMob app ID in app.json:
{
"expo": {
"plugins": [
[
"react-native-google-mobile-ads",
{
"androidAppId": "ca-app-pub-3940256099942544~3347511713",
"iosAppId": "ca-app-pub-3940256099942544~1458002511"
}
]
]
}
}
💡 Tip: Use test ad unit IDs during development. Never click your own ads in production - this violates AdMob policies and can get your account banned.
Build a reusable banner ad:
// components/BannerAd.js
import React, { useState } from 'react';
import { View, Text, StyleSheet } from 'react-native';
const BannerAd = ({ adUnitId, size = 'BANNER' }) => {
const [loaded, setLoaded] = useState(false);
const [error, setError] = useState(null);
// Simulated banner ad (replace with actual AdMob component)
// import { BannerAd as AdMobBanner, BannerAdSize, TestIds } from 'react-native-google-mobile-ads';
return (
<View style={styles.container}>
{/* Placeholder for ad */}
<View style={styles.adPlaceholder}>
<Text style={styles.adText}>📢 Advertisement</Text>
<Text style={styles.adSubtext}>
Banner Ad ({size})
</Text>
</View>
{/* In production:
<AdMobBanner
unitId={adUnitId || TestIds.BANNER}
size={BannerAdSize[size]}
onAdLoaded={() => setLoaded(true)}
onAdFailedToLoad={(error) => setError(error)}
/>
*/}
</View>
);
};
const styles = StyleSheet.create({
container: {
backgroundColor: '#f5f5f5',
borderTopWidth: 1,
borderTopColor: '#e0e0e0',
},
adPlaceholder: {
height: 50,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: '#f9f9f9',
},
adText: {
fontSize: 12,
color: '#999',
},
adSubtext: {
fontSize: 10,
color: '#ccc',
marginTop: 2,
},
});
export default BannerAd;
Manage full-screen ads between actions:
// services/InterstitialAdManager.js
export class InterstitialAdManager {
constructor() {
this.ad = null;
this.loaded = false;
this.showCount = 0;
this.lastShownTime = 0;
this.minInterval = 60000; // Minimum 60 seconds between ads
}
async initialize(adUnitId) {
console.log('Initializing interstitial ad');
// In production: create and load InterstitialAd
// import { InterstitialAd, TestIds } from 'react-native-google-mobile-ads';
// this.ad = InterstitialAd.createForAdRequest(adUnitId || TestIds.INTERSTITIAL);
await this.loadAd();
}
async loadAd() {
try {
// Simulate ad loading
await new Promise(resolve => setTimeout(resolve, 500));
this.loaded = true;
console.log('Interstitial ad loaded');
} catch (error) {
console.error('Failed to load interstitial:', error);
this.loaded = false;
}
}
async show() {
// Check frequency capping
const now = Date.now();
if (now - this.lastShownTime < this.minInterval) {
console.log('Skipping ad - too soon since last ad');
return false;
}
if (!this.loaded) {
console.log('Ad not loaded yet');
return false;
}
try {
// Show ad
console.log('Showing interstitial ad');
this.showCount++;
this.lastShownTime = now;
this.loaded = false;
// Reload next ad
await this.loadAd();
return true;
} catch (error) {
console.error('Failed to show interstitial:', error);
return false;
}
}
shouldShowAd(actionCount) {
// Show ad every 3-5 actions
return actionCount % 4 === 0;
}
}
Build rewarded ads that grant benefits:
// components/RewardedVideoButton.js
import React, { useState } from 'react';
import {
TouchableOpacity,
Text,
StyleSheet,
ActivityIndicator,
Alert,
} from 'react-native';
const RewardedVideoButton = ({ onReward, rewardAmount = 100 }) => {
const [loading, setLoading] = useState(false);
const handlePress = async () => {
setLoading(true);
try {
// Simulate video ad
await new Promise(resolve => setTimeout(resolve, 2000));
// Grant reward
Alert.alert(
'🎉 Reward Earned!',
`You earned ${rewardAmount} coins for watching the video!`,
[
{
text: 'Awesome!',
onPress: () => onReward(rewardAmount),
},
]
);
} catch (error) {
Alert.alert('Error', 'Failed to load video ad. Please try again.');
} finally {
setLoading(false);
}
};
return (
<TouchableOpacity
style={styles.button}
onPress={handlePress}
disabled={loading}
>
{loading ? (
<ActivityIndicator color="white" />
) : (
<>
<Text style={styles.icon}>▶️</Text>
<Text style={styles.buttonText}>
Watch Ad for {rewardAmount} Coins
</Text>
</>
)}
</TouchableOpacity>
);
};
const styles = StyleSheet.create({
button: {
backgroundColor: '#4ECDC4',
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'center',
padding: 16,
borderRadius: 12,
gap: 10,
},
icon: {
fontSize: 20,
},
buttonText: {
color: 'white',
fontSize: 16,
fontWeight: 'bold',
},
});
export default RewardedVideoButton;
Create smart ad placement logic:
// App.js
import React, { useState, useEffect } from 'react';
import { View, Text, ScrollView, TouchableOpacity, StyleSheet } from 'react-native';
import BannerAd from './components/BannerAd';
import RewardedVideoButton from './components/RewardedVideoButton';
import { InterstitialAdManager } from './services/InterstitialAdManager';
export default function App() {
const [coins, setCoins] = useState(0);
const [actionCount, setActionCount] = useState(0);
const [isPremium, setIsPremium] = useState(false);
const [interstitialManager] = useState(new InterstitialAdManager());
useEffect(() => {
if (!isPremium) {
interstitialManager.initialize();
}
}, [isPremium]);
const handleAction = async () => {
setActionCount(prev => prev + 1);
// Show interstitial ad periodically (if not premium)
if (!isPremium && interstitialManager.shouldShowAd(actionCount + 1)) {
await interstitialManager.show();
}
// Perform action logic
console.log('Action performed');
};
const handleReward = (amount) => {
setCoins(prev => prev + amount);
};
return (
<View style={styles.container}>
<ScrollView style={styles.content}>
<View style={styles.header}>
<Text style={styles.headerTitle}>Coin Game</Text>
<View style={styles.coinDisplay}>
<Text style={styles.coinIcon}>🪙</Text>
<Text style={styles.coinText}>{coins}</Text>
</View>
</View>
<View style={styles.section}>
<Text style={styles.sectionTitle}>Earn Free Coins</Text>
<RewardedVideoButton
onReward={handleReward}
rewardAmount={100}
/>
</View>
<View style={styles.section}>
<Text style={styles.sectionTitle}>Actions</Text>
<TouchableOpacity
style={styles.actionButton}
onPress={handleAction}
>
<Text style={styles.actionButtonText}>
Perform Action ({actionCount})
</Text>
</TouchableOpacity>
<Text style={styles.hint}>
💡 Interstitial ads show every 4 actions
</Text>
</View>
{isPremium ? (
<View style={styles.premiumBanner}>
<Text style={styles.premiumText}>
✨ Premium Active - No Ads!
</Text>
</View>
) : (
<TouchableOpacity
style={styles.upgradeBanner}
onPress={() => setIsPremium(true)}
>
<Text style={styles.upgradeText}>
Remove Ads - Go Premium
</Text>
</TouchableOpacity>
)}
</ScrollView>
{/* Show banner ad only if not premium */}
{!isPremium && <BannerAd />}
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#f5f5f5',
},
content: {
flex: 1,
},
header: {
flexDirection: 'row',
justifyContent: 'space-between',
alignItems: 'center',
padding: 20,
backgroundColor: 'white',
},
headerTitle: {
fontSize: 28,
fontWeight: 'bold',
},
coinDisplay: {
flexDirection: 'row',
alignItems: 'center',
backgroundColor: '#FFD93D',
paddingHorizontal: 15,
paddingVertical: 8,
borderRadius: 20,
},
coinIcon: {
fontSize: 20,
marginRight: 5,
},
coinText: {
fontSize: 18,
fontWeight: 'bold',
},
section: {
padding: 20,
},
sectionTitle: {
fontSize: 20,
fontWeight: 'bold',
marginBottom: 15,
},
actionButton: {
backgroundColor: '#4ECDC4',
padding: 16,
borderRadius: 12,
alignItems: 'center',
},
actionButtonText: {
color: 'white',
fontSize: 16,
fontWeight: 'bold',
},
hint: {
fontSize: 12,
color: '#666',
marginTop: 10,
textAlign: 'center',
},
premiumBanner: {
margin: 20,
padding: 20,
backgroundColor: '#FFD93D',
borderRadius: 12,
alignItems: 'center',
},
premiumText: {
fontSize: 16,
fontWeight: 'bold',
},
upgradeBanner: {
margin: 20,
padding: 20,
backgroundColor: '#4ECDC4',
borderRadius: 12,
alignItems: 'center',
},
upgradeText: {
color: 'white',
fontSize: 16,
fontWeight: 'bold',
},
});
Problem: Ads not loading Solution: Use test ad unit IDs during development. Check internet connection. Verify AdMob account is active and app is approved.
Problem: Banner ad causes layout shift Solution: Reserve space for banner before it loads. Use fixed height container. Consider collapsible banner that expands when loaded.
Problem: Too many ads annoying users Solution: Implement frequency capping (max X ads per hour). Show interstitials only at natural breaks. Monitor user retention metrics.
Problem: Rewarded video not granting reward Solution: Only grant reward in onRewarded callback. Verify video completed fully. Handle network errors gracefully.
Problem: Account disabled for invalid traffic Solution: Never click your own ads. Use test IDs during development. Don't encourage users to click ads. Monitor invalid traffic reports.
For advanced students:
Native Ads: Integrate native ads that match your app's design
Ad Mediation: Add multiple ad networks for better fill rates
A/B Testing: Test different ad frequencies to optimize revenue vs retention
Ad-Free Weekends: Temporarily disable ads as a promotional feature
Smart Timing: Show interstitials only when user completes valuable action
In this activity, you:
In the next lesson, you'll explore viral growth patterns and social sharing. You'll learn how to make your app spreadable through share sheets, deep linking, and referral programs.