Exposed Pause Ad
The Pause Ad integration enables high-impact, non-intrusive advertising within the StreamLayer Web SDK. When a user pauses a video, the SDK handles the asynchronous loading and rendering of a VAST-compliant ad overlay and GPT ads (Google Publisher Tag ads loaded in a sandboxed iframe via StreamLayer's ad host). It features a robust state management system to help developers synchronize their video player UI with the ad’s lifecycle, ensuring that interactive controls (like play/resume) and animations work seamlessly on desktop.
Alpha Version: This feature is currently in alpha. Expect potential instability, including occasional freezes.
Overview
The Pause Ad feature displays an advertisement overlay when the user pauses video playback. It supports two ad variants:
- VAST ads — Server-fetched static image ads via the SDK's external pause ad store
- GPT ads — Google Publisher Tag ads loaded in a sandboxed iframe via StreamLayer's ad host
Integration
Import UI Component
Import the StreamLayerPauseAd component from the @streamlayer/react/pause-ad package:
import { StreamLayerPauseAd } from '@streamlayer/react/pause-ad'Standalone Usage (Without Wrapping Video)
StreamLayerPauseAd accepts optional children. If you don't want to wrap your video element, you can render it as a standalone sibling alongside your video. No children are required — the component works on its own.
<App>
{/* Your video — not wrapped by the SDK */}
<div className="video-container">
<video ref={videoRef} onPause={onVideoPause} onPlay={onVideoPlay} />
</div>
<StreamLayerProvider sdkKey="your-sdk-key" event="your-event-id">
{/* Pause ad rendered independently */}
<StreamLayerPauseAd
showPauseAd={showPauseAd}
onRenderPauseAd={onRenderPauseAd}
onClosePauseAd={onClosePauseAd}
videoPlayerController={videoPlayerController}
pauseAdExternalUrls={[{ template: 'default', url: 'your-ad-url' }]}
/>
</StreamLayerProvider>
</App>When used this way, you control the pause ad's size and position through CSS by targeting the .SL_PauseAd class name:
.SL_PauseAd {
width: 300px !important;
left: auto !important;
}You can inspect the rendered pause ad element in your browser's developer tools to discover additional internal class names for further styling.
Ad Configuration
VAST Ad
Pass a VAST tag URL via pauseAdExternalUrls:
<StreamLayerPauseAd
pauseAdExternalUrls={[
{ template: 'default', url: 'https://your-vast-tag-url.com/vast.xml' },
]}
// ...other props
/>Note: Only the first item in the array is used. Multi-item rotation is not yet supported.
GPT Ad (Slot-Based) — Recommended
Pass a GPT slot ID (non-HTTP path) via pauseAdExternalUrls. The SDK detects it as a GPT ad because the URL does not start with http:
<StreamLayerPauseAd
pauseAdExternalUrls={[
{
url: '/12345/ad-unit-name', // Non-HTTP = treated as GPT slot ID
adWidth: 315,
adHeight: 400,
npa: 0,
is_lat: 0,
rdid: 'device-advertising-id',
idtype: 'idfa', // Roku = RIDA, Samsung = TIFA, Android = adid, tvOS = idfa
targeting: { genre: 'sports', league: 'nba' },
},
]}
// ...other props
/>GPT Ad (URL-Based)
Pass a Google Ad Manager URL with output=ldjh query param via pauseAdExternalUrls:
<StreamLayerPauseAd
pauseAdExternalUrls={[
{
url: 'https://pagead2.googlesyndication.com/gampad/ads?...&output=ldjh', // output=ldjh = GPT
product: 'tsn',
platform: 'cotv',
platformtype: 'amazonfire',
pagetype: 'playerpage',
content: 'cfl-news-and-highlights',
npa: 0,
is_lat: 0,
rdid: 'device-advertising-id',
idtype: 'idfa', // Roku = RIDA, Samsung = TIFA, Android = adid, tvOS = idfa
targeting: { genre: 'sports', league: 'nba' },
},
]}
// ...other props
/>Ad Type Detection
The SDK automatically determines which backend to use based on the URL:
- GPT — if the URL does not start with
http(slot ID) or contains theoutput=ldjhquery param - VAST — otherwise
Props
| Prop | Type | Description |
|---|---|---|
showPauseAd | boolean | Controls pause ad visibility. Set to true when video pauses. |
onRenderPauseAd | (params: { rendered: boolean }) => void | Called when pause ad overlay renders. Use to hide your UI elements. |
onClosePauseAd | () => void | Called when pause ad closes without resuming video (user dismissed overlay). |
videoPlayerController | VideoPlayerCallback | Called when user resumes video via the pause ad overlay button. |
pauseAdExternalUrls | PauseAdExternalUrl[] | Array of ad configurations (supports VAST and GPT). See below. |
options | StreamLayerPauseAdOptions | Optional configuration (see Options below). |
pauseAdExternalUrls Fields
pauseAdExternalUrls Fields| Field | Type | Description |
|---|---|---|
url | string | Required. VAST URL, GPT URL (output=ldjh), or GPT slot ID (non-HTTP path) |
product | string? | Product identifier (e.g., tsn). Used for ad unit path building |
platform | string? | Platform identifier (e.g., cotv). Used for ad unit path building |
platformtype | string? | Platform type (e.g., amazonfire). Used for ad unit path building |
pagetype | string? | Page type (e.g., playerpage). Used for ad unit path building |
content | string? | Content identifier (e.g., cfl-news-and-highlights). Used for ad unit path building |
adWidth | number? | Custom ad width for GPT iframe |
adHeight | number? | Custom ad height for GPT iframe |
npa | 0 | 1 | Non-personalized ads flag (1 = non-personalized). When npa=1, permutive targeting keys are stripped |
is_lat | 0 | 1 | Limit ad tracking flag (1 = limited) |
rdid | string? | Device advertising ID (e.g., Roku RIDA, Samsung TIFA, Android adid, tvOS idfa) |
idtype | string? | Device ID type identifier (e.g., RIDA, TIFA, adid, idfa) |
targeting | Record<string, string>? | Custom GPT key-value targeting pairs (passed as t= param to the ad host) |
Options
type StreamLayerPauseAdOptions = {
showPauseButton?: boolean // toggle render our pause ad button or use your button, default is true
pauseAdDelay?: number // delay in ms before showing pause ad after video pause, default is 0 (no delay)
pauseAdRefetchInterval?: number // background refetch interval in ms, used only when pauseAdDelay is 0
}How It Works
VAST Path
showPauseAdbecomestrue→ SDK fetches ad from the VAST URL- Store loads and returns ad data with
imageSrcandadUrl - Static image ad is rendered in the overlay
- On desktop, the image is wrapped in a click-through link
- Emits
renderedevent viaonRenderPauseAd
GPT Path
showPauseAdbecomestrue→ component renders immediately (no store fetch)- An iframe is created pointing to the StreamLayer ad host.
- URL params include targeting data (
product,platform,platformtype,pagetype,content), privacy flags (npa,is_lat), custom targeting (t), device ID (rdid/idtype), and eitherurlorslot. - Iframe communicates via
postMessage:slGptAdLoaded— ad creative loaded (includes dimensions)slGptAdReady— ad fully rendered
- Iframe is scaled to fit the container via
ResizeObserver+ CSStransform: scale() - 10s safety timeout — if
slGptAdLoadedisn't received within 10 seconds, the ad auto-closes
Ad Loading Strategies
The SDK supports two ad loading strategies depending on the pauseAdDelay and pauseAdRefetchInterval options. This gives you control over whether ads are loaded on-demand when the user pauses, or prefetched in the background for instant display.
On-Demand Loading
When pauseAdDelay is set to a value greater than 0, the SDK does not prefetch or refresh ads in the background. Instead, the ad is loaded during the delay between the video pause and the pause ad display.
How it works:
- User pauses the video,
showPauseAdis set totrue. - The SDK starts the
pauseAdDelaytimer and simultaneously begins loading the VAST ad. - If the ad finishes loading before the timer expires, the ad is displayed when the timer completes.
- If the ad has not finished loading when the timer expires, the SDK continues waiting without rendering — the ad is displayed as soon as loading completes.
- If no ad is available (empty VAST response or load failure), nothing is rendered.
Video pauses
│
▼
pauseAdDelay timer starts + ad load begins
│
├── Ad loads before timer expires → show ad when timer completes
│
└── Timer expires before ad loads → continue waiting → show ad when load completes
(or skip if no ad available)options={{
pauseAdDelay: 5000, // load ad during this 5s delay
}}Prefetch with Background Refresh
When pauseAdDelay is set to 0 and pauseAdRefetchInterval is defined, the SDK prefetches the ad immediately on initialization and keeps it fresh in the background.
How it works:
- On SDK initialization, the VAST ad is loaded and cached immediately.
- The SDK refreshes the cached ad in the background at the specified
pauseAdRefetchInterval. - When the user pauses the video, the cached ad is displayed instantly (no delay).
- After the ad is displayed, the cache is invalidated and a new ad is loaded right away — this guarantees the user sees a new advertisement every time.
- The background refetch interval restarts after the new ad is loaded.
Because the cache is always invalidated after each display, the pauseAdRefetchInterval only serves as a background keep-alive to ensure the cached ad stays fresh between pauses. You do not need a short interval to guarantee fresh ads — that is already handled by the post-display invalidation.
Recommended: Set
pauseAdRefetchIntervalto a high value (up to 1 hour). Google restricts VAST ad caching to a maximum of 1 hour, so3600000(1 hour) is the upper limit. A longer interval reduces unnecessary network requests and lowers costs while still keeping the cache ready for instant display.
SDK initializes
│
▼
Load ad → cache ready
│
├── refetch interval ──→ refresh cache ──→ refetch interval ──→ ...
│
▼
Video pauses → show cached ad instantly
│
▼
Invalidate cache → load new ad immediately (guarantees fresh ad next time)
│
▼
Restart refetch intervaloptions={{
pauseAdDelay: 0, // no delay — use prefetched ad
pauseAdRefetchInterval: 3300000, // refresh cached ad every 55 minutes (Google max is 1 hour)
}}Note: Even if both
pauseAdDelayandpauseAdRefetchIntervalare set to0, there will still be a minimal delay before the pause ad appears. This delay is the time the SDK needs to load and prepare the ad content for rendering.
Choosing a Strategy
| Strategy | pauseAdDelay | pauseAdRefetchInterval | Best For |
|---|---|---|---|
| On-Demand | > 0 | Not set | Lower bandwidth usage; acceptable to wait before showing ad |
| Prefetch | 0 (default) | Set (e.g. 3300000) | Instant ad display; always-ready ad experience |
Event Lifecycle
The pause ad emits lifecycle events via callbacks:
| Event | When |
|---|---|
enabled | showPauseAd becomes true |
disabled | showPauseAd becomes false (without explicit close) |
closed | User explicitly dismisses the ad |
rendered | Ad content is visible (image loaded or GPT iframe ready) |
navigated | User clicks the ad link (VAST desktop only) |
UIState Integration
When the pause ad is active:
- Sidebar content is hidden
- Overlay promotions are hidden
- Banner ads are hidden
- Notification overlays are hidden
This ensures no visual conflicts between the pause ad and other SDK UI.
Understanding showPauseAd vs pauseAdRendered
showPauseAd vs pauseAdRenderedThese two flags serve different purposes and are controlled by different parties:
| Flag | Controlled By | Purpose |
|---|---|---|
showPauseAd | You (integrator) | Request to show/hide pause ad |
pauseAdRendered | SDK (via callback) | Actual visibility state on screen |
Why two flags?
showPauseAd is your intent — "I want the pause ad to appear/disappear."
pauseAdRendered is the actual state — "The pause ad is currently visible on screen."
These values don't always match due to animation delays:
- Opening delay: SDK waits for the configured
pauseAdDelaybefore showing the StreamLayer Element - Closing delay (400ms): SDK animates the StreamLayer Element before removing it
State combinations
showPauseAd | pauseAdRendered | State |
|---|---|---|
false | false | Video playing or paused without ad |
true | false | Video paused, SDK preparing StreamLayer Element |
true | true | Pause ad visible on screen |
false | true | Closing animation in progress (400ms) |
Timeline
Video pauses
│
▼
showPauseAd = true ← You set this on video pause
pauseAdRendered = false
│
│ ← pauseAdDelay (SDK loads/retrieves ad, prepares overlay)
▼
showPauseAd = true
pauseAdRendered = true ← SDK calls onRenderPauseAd({ rendered: true })
Hide your UI controls now
│
│ ← User resumes video or closes overlay
▼
showPauseAd = false ← You set this on play/close
pauseAdRendered = true ← Still true! Closing animation playing
│
│ ← 400ms closing animation
▼
showPauseAd = false
pauseAdRendered = false ← SDK calls onRenderPauseAd({ rendered: false })
Safe to show your UI controlsWhy this matters
Always use pauseAdRendered to control your UI visibility:
// Correct: hide UI based on actual render state
const showControls = !pauseAdRendered
// This ensures:
// 1. Your controls don't disappear before the ad appears
// 2. Your controls don't reappear during the 400ms closing animationIf you used showPauseAd instead, your controls would:
- Disappear immediately on pause (before the ad actually shows)
- Reappear during the closing animation (overlapping with the ad)
Implementation Example
import { useState, useCallback, useRef } from 'react'
import { StreamLayerProvider, type VideoPlayerCallback } from '@streamlayer/react'
import { StreamLayerPauseAd } from '@streamlayer/react/pause-ad'
export const VideoView = () => {
const [showPauseAd, setShowPauseAd] = useState(false)
const [pauseAdRendered, setPauseAdRendered] = useState(false)
const videoRef = useRef<HTMLVideoElement>(null)
// Called when pause ad overlay renders/hides
const onRenderPauseAd = useCallback((params: { rendered: boolean }) => {
setPauseAdRendered(params.rendered)
}, [])
// Called when pause ad closes without video resumption
const onClosePauseAd = useCallback(() => {
setShowPauseAd(false)
}, [])
// Called when user clicks resume on pause ad overlay
const videoPlayerController: VideoPlayerCallback = useCallback((data) => {
if (data.play === true) {
videoRef.current?.play()
}
}, [])
// Video event handlers
const onVideoPause = useCallback(() => {
setShowPauseAd(true)
}, [])
const onVideoPlay = useCallback(() => {
setShowPauseAd(false)
}, [])
// Hide your UI controls when pause ad is rendered
const showControls = !pauseAdRendered
return (
<StreamLayerProvider sdkKey="your-sdk-key" event="your-event-id">
<StreamLayerPauseAd
showPauseAd={showPauseAd}
onRenderPauseAd={onRenderPauseAd}
onClosePauseAd={onClosePauseAd}
videoPlayerController={videoPlayerController}
pauseAdExternalUrls={[
{
template: 'default',
url: 'https://your-vast-tag-url.com',
},
]}
>
{/* Your video container */}
<div>
<video
ref={videoRef}
onPause={onVideoPause}
onPlay={onVideoPlay}
/>
{/* Hide controls when pause ad is displayed */}
{showControls && (
<div className="video-controls">
{/* Play button, timeline, etc. */}
</div>
)}
</div>
</StreamLayerPauseAd>
</StreamLayerProvider>
)
}Key Points
- You control
showPauseAd: Set it totrueon video pause,falseon play/resume. - Hide UI on render: When
onRenderPauseAdfires withrendered: true, hide your video controls (play button, timeline, etc.). - Handle both exit paths: User can either resume video (via
videoPlayerController) or dismiss the overlay (viaonClosePauseAd). - Two ad backends: Use
pauseAdExternalUrlsfor VAST ads orpauseAdExternalUrlsfor GPT ads (slot-based or URL-based). - Ad type auto-detection: The SDK automatically determines VAST vs GPT based on the URL format.
Related
- Advertising — Advertisement component integration
- Video Controller — Volume control during SDK events
- Integration Guide — Full Web SDK setup and configuration
Updated 3 days ago
