Implement client-side ad insertion using the JS approach
In this tutorial, you'll implement client-side ad insertion (CSAI) in your Vega app using the JS approach. This approach uses direct React Native component integration where VideoPlayer instances are created and managed within the UI component layer. You'll set up a dual-player architecture with a main player for content and an ad player that takes over a shared video surface during ad breaks.
The examples in this tutorial demonstrate pre-buffered ads for seamless ad transitions. For background on CSAI concepts, techniques, and sync strategies, see Client-side ad insertion in Vega.
In this tutorial, you learn how to:
- Set up player references and imports
- Detect an ad marker and pre-initialize the ad player
- Wait for the ad to buffer, then pause main content
- Switch the video surface and play the ad
- Return to main content after the ad ends
Prerequisites
- Vega development environment set up
- Vega SDK v0.22 or later
@amazon-devices/react-native-w3cmediapackage installed- Main content player already initialized and playing
- Basic knowledge of React Native and TypeScript
Set up player references
Import the required components and create refs for the main player, ad player, and shared surface handle.
import { VideoPlayer, KeplerVideoSurfaceView } from '@amazon-devices/react-native-w3cmedia';
// Player references
const videoPlayer = useRef<VideoPlayer | null>(null); // Main content player (already initialized)
const adVideoPlayer = useRef<VideoPlayer | null>(null); // Ad player
const cachedSurface = useRef<any>(null); // Shared surface handle
Detect the ad marker and pre-initialize the ad player
When the app detects an upcoming ad break (for example, 5 seconds before the ad start time), pre-initialize the ad player and begin buffering the ad content with autoPlay=false.
- Register a
timeupdatelistener on the main player to detect upcoming ad breaks.
videoPlayer.current.addEventListener('timeupdate', () => {
const currentTime = videoPlayer.current?.currentTime || 0;
// App-specific logic to determine if an ad break is approaching
// (e.g., VMAP schedule, sidecar file, hardcoded list, or ad SDK callback)
if (adBreakApproaching(currentTime)) {
initializeAdPlayer(AD_CONTENT_URL);
}
});
- Create the ad player initialization function. Set
autoPlaytofalseso you control when playback starts.
const initializeAdPlayer = async (adUrl: string) => {
adVideoPlayer.current = new VideoPlayer();
await adVideoPlayer.current.initialize();
adVideoPlayer.current.autoplay = false;
adVideoPlayer.current.addEventListener('canplay', onAdReady);
adVideoPlayer.current.addEventListener('ended', onAdEnded);
adVideoPlayer.current.src = adUrl;
};
Wait for the ad to buffer, then pause main content
After the ad player signals it is buffered (canplay) and the ad start time is reached, pause the main content.
const onAdReady = () => {
// Ad is buffered — wait for the scheduled ad start time
// When the position listener detects ad start time is reached:
videoPlayer.current?.pause();
};
Switch the surface and play the ad
When the main content pauses, clear the main player's surface and assign it to the ad player.
videoPlayer.current.addEventListener('pause', () => {
// Clear main player surface and assign to ad player
videoPlayer.current?.clearSurfaceHandle(cachedSurface.current);
adVideoPlayer.current?.setSurfaceHandle(cachedSurface.current);
adVideoPlayer.current?.play();
});
Return to main content
After the ad finishes, clear the ad surface, clean up the ad player, and resume main content.
const onAdEnded = async () => {
adVideoPlayer.current?.clearSurfaceHandle(cachedSurface.current);
videoPlayer.current?.setSurfaceHandle(cachedSurface.current);
videoPlayer.current?.play();
await adVideoPlayer.current?.deinitialize();
adVideoPlayer.current = null;
};
Clean up resources
When your component unmounts or the player session ends, deinitialize both players and release the surface handle.
const cleanup = async () => {
if (adVideoPlayer.current) {
await adVideoPlayer.current.deinitialize();
adVideoPlayer.current = null;
}
// Main player cleanup handled by your existing teardown logic
};
Related content
- Client-side ad insertion in Vega
- Implement client-side ad insertion using the Headless approach
- W3C Media Player documentation
Last updated: Mar 13, 2026

