React Native Integration Guide
Integrate AppSpike into your React Native app and serve your first ad in under 10 minutes.
AppSpike sits on top of your existing ad networks and uses real-time bidding to route every impression to the highest-paying demand source. You keep your current ad network accounts — AppSpike optimizes across all of them.
Prerequisites
- React Native 0.72+ (New Architecture supported)
- Node.js 18+
- iOS 13+ / Android API 21+
- TypeScript 5.0+ (recommended)
- An AppSpike account and App ID (sign up)
Step 1: Install the SDK
Install the AppSpike React Native package:
npmnpm install @appspike/react-native
yarn
yarn add @appspike/react-native
Android Setup
Add the AppSpike Maven repository to your project-level android/build.gradle:
allprojects {
repositories {
google()
mavenCentral()
maven { url 'https://sdk.appspike.dev/android' }
}
}
iOS Setup
Install CocoaPods dependencies:
cd ios && pod install && cd ..
Add the NSUserTrackingUsageDescription key to your ios/<YourApp>/Info.plist for App Tracking Transparency compliance:
<key>NSUserTrackingUsageDescription</key>
<string>This allows us to show you more relevant ads.</string>
Step 2: Initialize the SDK
Initialize AppSpike as early as possible, ideally in your root component or entry point. Pass your App ID directly:
App.tsximport React, { useEffect } from 'react';
import { AppSpike } from '@appspike/react-native';
const App: React.FC = () => {
useEffect(() => {
AppSpike.initialize({ appId: "YOUR_APP_ID" })
.then((result) => {
console.log(`AppSpike initialized — ${result.adapterCount} adapters loaded`);
})
.catch((error) => {
console.error(`AppSpike init failed: ${error.message}`);
});
}, []);
return (
// Your app content
);
};
export default App;
Step 3: Banner Ads
Use the <AppSpikeBanner /> component to render banner ads declaratively:
import React from 'react';
import { View, StyleSheet } from 'react-native';
import { AppSpikeBanner, BannerAdSize } from '@appspike/react-native';
const HomeScreen: React.FC = () => {
return (
<View style={styles.container}>
{/* Your screen content */}
<View style={styles.content}>
{/* ... */}
</View>
{/* Banner pinned to bottom */}
<AppSpikeBanner
placementId="YOUR_APPSPIKE_PLACEMENT"
size={BannerAdSize.BANNER}
onAdLoaded={() => console.log('Banner loaded')}
onAdFailedToLoad={(error) => console.error(`Banner failed: ${error.message}`)}
onAdClicked={() => console.log('Banner clicked')}
style={styles.banner}
/>
</View>
);
};
const styles = StyleSheet.create({
container: { flex: 1 },
content: { flex: 1 },
banner: { alignSelf: 'center' },
});
export default HomeScreen;
Available BannerAdSize options: BANNER, LARGE_BANNER, MEDIUM_RECTANGLE, FULL_BANNER, LEADERBOARD, ADAPTIVE.
Step 4: Interstitial Ads
Use the useInterstitialAd hook to load and show interstitials at natural break points:
import React, { useEffect } from 'react';
import { Button, View } from 'react-native';
import { useInterstitialAd } from '@appspike/react-native';
const GameScreen: React.FC = () => {
const { load, show, isLoaded, isClosed } = useInterstitialAd("YOUR_APPSPIKE_PLACEMENT");
useEffect(() => {
load();
}, [load]);
// Reload after the ad is dismissed
useEffect(() => {
if (isClosed) {
load();
}
}, [isClosed, load]);
const handleLevelComplete = () => {
if (isLoaded) {
show();
}
// Continue to next level
navigateToNextLevel();
};
return (
<View>
<Button title="Next Level" onPress={handleLevelComplete} />
</View>
);
};
export default GameScreen;
Step 5: Rewarded Ads
Use the useRewardedAd hook when the user opts in to watch an ad for a reward:
import React, { useEffect, useState } from 'react';
import { Button, Text, View } from 'react-native';
import { useRewardedAd } from '@appspike/react-native';
const StoreScreen: React.FC = () => {
const [coins, setCoins] = useState(0);
const { load, show, isLoaded, reward, isClosed } = useRewardedAd("YOUR_APPSPIKE_PLACEMENT");
useEffect(() => {
load();
}, [load]);
// Reload after dismissal
useEffect(() => {
if (isClosed) {
load();
}
}, [isClosed, load]);
// Grant reward when earned
useEffect(() => {
if (reward) {
console.log(`User earned: ${reward.amount} ${reward.type}`);
setCoins((prev) => prev + reward.amount);
}
}, [reward]);
return (
<View>
<Text>Coins: {coins}</Text>
<Button
title="Watch Ad for 50 Coins"
onPress={() => show()}
disabled={!isLoaded}
/>
</View>
);
};
export default StoreScreen;
Step 6: Native Ads
Use the useNativeAd hook to load native ads and bind them to your own custom layout:
import React, { useEffect } from 'react';
import { View, Text, Image, TouchableOpacity, StyleSheet } from 'react-native';
import { useNativeAd, NativeAdView } from '@appspike/react-native';
const NativeAdComponent: React.FC = () => {
const { load, ad, isLoaded } = useNativeAd("YOUR_APPSPIKE_PLACEMENT");
useEffect(() => {
load();
}, [load]);
if (!isLoaded || !ad) return null;
return (
<NativeAdView ad={ad} style={styles.container}>
<View style={styles.header}>
<Image source={{ uri: ad.iconUrl }} style={styles.icon} />
<View>
<Text style={styles.headline}>{ad.headline}</Text>
<Text style={styles.body}>{ad.body}</Text>
</View>
</View>
<TouchableOpacity style={styles.cta}>
<Text style={styles.ctaText}>{ad.callToAction}</Text>
</TouchableOpacity>
</NativeAdView>
);
};
const styles = StyleSheet.create({
container: { padding: 16, backgroundColor: '#f9f9f9', borderRadius: 12 },
header: { flexDirection: 'row', gap: 12, marginBottom: 12 },
icon: { width: 48, height: 48, borderRadius: 8 },
headline: { fontSize: 16, fontWeight: '700' },
body: { fontSize: 14, color: '#666' },
cta: { backgroundColor: '#4A9EFF', padding: 12, borderRadius: 8, alignItems: 'center' },
ctaText: { color: '#fff', fontWeight: '600' },
});
export default NativeAdComponent;
Step 7: App Open Ads
App open ads display when users bring your app to the foreground. Use the useAppOpenAd hook with React Native's AppState listener:
import React, { useEffect, useRef } from 'react';
import { AppState, AppStateStatus } from 'react-native';
import { useAppOpenAd } from '@appspike/react-native';
const AppOpenAdManager: React.FC = () => {
const appState = useRef(AppState.currentState);
const { load, show, isLoaded, isClosed } = useAppOpenAd("YOUR_APPSPIKE_PLACEMENT");
useEffect(() => {
load();
}, [load]);
// Reload after dismissal
useEffect(() => {
if (isClosed) {
load();
}
}, [isClosed, load]);
useEffect(() => {
const subscription = AppState.addEventListener('change', (nextState: AppStateStatus) => {
if (appState.current.match(/inactive|background/) && nextState === 'active') {
if (isLoaded) {
show();
}
}
appState.current = nextState;
});
return () => subscription.remove();
}, [isLoaded, show]);
return null; // This component manages ads, no UI needed
};
export default AppOpenAdManager;
Mount this component at the root of your app:
App.tsxconst App: React.FC = () => {
return (
<>
<AppOpenAdManager />
<NavigationContainer>
{/* Your screens */}
</NavigationContainer>
</>
);
};
Step 8: Rewarded Interstitial Ads
Rewarded interstitials combine the full-screen experience of an interstitial with an opt-in reward. Unlike standard rewarded ads, they can be shown without the user explicitly choosing to watch:
LevelTransition.tsximport React, { useEffect, useState } from 'react';
import { Button, Text, View } from 'react-native';
import { useRewardedInterstitialAd } from '@appspike/react-native';
const LevelTransition: React.FC = () => {
const [bonusCoins, setBonusCoins] = useState(0);
const { load, show, isLoaded, reward, isClosed } = useRewardedInterstitialAd("YOUR_APPSPIKE_PLACEMENT");
useEffect(() => {
load();
}, [load]);
// Reload after dismissal
useEffect(() => {
if (isClosed) {
load();
}
}, [isClosed, load]);
// Grant reward when earned
useEffect(() => {
if (reward) {
console.log(`User earned: ${reward.amount} ${reward.type}`);
setBonusCoins((prev) => prev + reward.amount);
}
}, [reward]);
const handleContinue = () => {
if (isLoaded) {
show();
}
navigateToNextLevel();
};
return (
<View>
<Text>Bonus Coins: {bonusCoins}</Text>
<Button title="Continue" onPress={handleContinue} />
</View>
);
};
export default LevelTransition;
Context / Provider Pattern
For production apps, use an AppSpikeProvider to centralize ad management across your entire app:
import React, { createContext, useContext, useEffect, useState, useCallback } from 'react';
import type { ReactNode } from 'react';
import { AppSpike, useInterstitialAd, useRewardedAd } from '@appspike/react-native';
interface AdContextType {
isInitialized: boolean;
showInterstitial: () => void;
showRewarded: (onReward: (amount: number) => void) => void;
isInterstitialReady: boolean;
isRewardedReady: boolean;
}
const AdContext = createContext<AdContextType | null>(null);
export const useAds = (): AdContextType => {
const context = useContext(AdContext);
if (!context) throw new Error('useAds must be used within AppSpikeProvider');
return context;
};
interface Props {
appId: string;
children: ReactNode;
}
export const AppSpikeProvider: React.FC<Props> = ({ appId, children }) => {
const [isInitialized, setIsInitialized] = useState(false);
const interstitial = useInterstitialAd("YOUR_APPSPIKE_PLACEMENT");
const rewarded = useRewardedAd("YOUR_APPSPIKE_PLACEMENT");
useEffect(() => {
AppSpike.initialize({ appId })
.then(() => setIsInitialized(true))
.catch((err) => console.error(`AppSpike init failed: ${err.message}`));
}, [appId]);
// Load ads once initialized
useEffect(() => {
if (isInitialized) {
interstitial.load();
rewarded.load();
}
}, [isInitialized]);
// Reload after dismissal
useEffect(() => {
if (interstitial.isClosed) interstitial.load();
}, [interstitial.isClosed]);
useEffect(() => {
if (rewarded.isClosed) rewarded.load();
}, [rewarded.isClosed]);
const showInterstitial = useCallback(() => {
if (interstitial.isLoaded) interstitial.show();
}, [interstitial.isLoaded, interstitial.show]);
const showRewarded = useCallback((onReward: (amount: number) => void) => {
if (rewarded.isLoaded) {
rewarded.show();
}
}, [rewarded.isLoaded, rewarded.show]);
return (
<AdContext.Provider
value={{
isInitialized,
showInterstitial,
showRewarded,
isInterstitialReady: interstitial.isLoaded,
isRewardedReady: rewarded.isLoaded,
}}
>
{children}
</AdContext.Provider>
);
};
Wrap your app:
App.tsxconst App: React.FC = () => {
return (
<AppSpikeProvider appId="YOUR_APP_ID">
<NavigationContainer>
{/* Your screens */}
</NavigationContainer>
</AppSpikeProvider>
);
};
Usage from any component:
LevelCompleteScreen.tsximport { useAds } from './AppSpikeProvider';
const LevelCompleteScreen: React.FC = () => {
const { showInterstitial, showRewarded, isRewardedReady } = useAds();
return (
<View>
<Button
title="Continue"
onPress={() => {
showInterstitial();
navigateToNextLevel();
}}
/>
<Button
title="Watch Ad for Bonus Coins"
onPress={() => showRewarded((amount) => addCoins(amount))}
disabled={!isRewardedReady}
/>
</View>
);
};
Testing
Enable test mode during development to avoid serving live ads:
AppSpike.initialize({ appId: "YOUR_APP_ID", testMode: true })
.then(() => {
// Test ads will always fill and never charge
});
Use the built-in ad inspector to debug mediation and demand source selection:
AppSpike.openAdInspector();
The ad inspector shows:
- Which adapters are initialized
- Bid responses per demand source
- Fill rates and latency per network
- Which network won each impression
Best Practices
- Pre-load interstitials and rewarded ads as early as possible, then show them at natural break points. Never interrupt the user mid-action.
- Always reload after dismissal. Each ad object is single-use — load the next one in the
isClosedeffect. - Ship with at least 3 ad network adapters. More demand sources means higher competition and higher eCPMs. AppSpike's mediation handles the rest.
- Use test mode during development. Live impressions from test devices waste ad budget and can flag your account.
- Don't gate core functionality behind rewarded ads. Use them for optional bonuses (extra lives, cosmetics, currency). Stores may reject apps that require ads to function.
- Use the Provider pattern to centralize ad state and avoid loading duplicates across screens.
- Handle navigation carefully. Unmounting a component with a loaded ad wastes that impression. Load ads in a persistent provider or root component.