Side-by-Side Ad
Integration
Follow these steps to add side-by-side ads to your application.
Step 1 — Install Packages
Install the StreamLayer React SDK:
npm install @streamlayer/react
Or with your preferred package manager:
# yarn
yarn add @streamlayer/react
# pnpm
pnpm add @streamlayer/react
Step 2 — Import Styles
Import the SDK stylesheet in your application entry point (e.g., App.tsx or index.tsx). This is required — without it the side-by-side layout, animations, and all other SDK UI will render without styles.
import '@streamlayer/react/style.css'
Step 3 — Import the Component
Import StreamLayerSDKAdvertisement from the @streamlayer/react/advertisement package:
import { StreamLayerSDKAdvertisement } from '@streamlayer/react/advertisement'
Step 4 — Set Up the Layout
Place StreamLayerSDKAdvertisement alongside your video player inside a wrapper element. Pass a ref to your video container via the videoRef prop. When a side-by-side ad activates, the SDK takes control of the video element's position and smoothly animates it into the left frame of a 50/50 split layout.
const videoContainerRef = useRef<HTMLDivElement>(null)
return (
<div id="player-root" style={{ position: 'relative' }}>
<div ref={videoContainerRef}>
<video src="https://example.com/stream.m3u8" autoPlay controls />
</div>
<StreamLayerProvider
sdkKey="your-sdk-key"
event="your-event-id"
containerId="player-root"
>
<StreamLayerSDKAdvertisement
sideBySide
videoRef={videoContainerRef}
muted={muted}
/>
</StreamLayerProvider>
</div>
)
Important: The video's parent container must have
position: relativefor correct absolute positioning.
Step 5 — Configure the Background Container
Set containerId on StreamLayerProvider to the HTML id of the outermost wrapper element that contains your video player and all StreamLayer SDK UI (ads, overlays, etc.).
When an advertisement is active, the SDK uses this container to apply the advertisement background image. It should be the same container you treat as the "player root" (the one that can be fullscreened/resized), not an inner ad-only element.
Important:
containerIdmust point to the outermost wrapper — the same element that holds both the video and the SDK components. Do not point it at an inner element.
Props
| Prop | Type | Description |
|---|---|---|
sideBySide | boolean | Required. Enables side-by-side ad mode. |
externalAd | boolean | Enables VAST/GAM external ad support. When true, the SDK renders VAST video ads. |
muted | boolean | Mutes the active ad video. Default: false. |
videoRef | React.RefObject<HTMLElement | null> | Required. Ref to the host video container for positioning. |
videoPlayerController | VideoPlayerCallback | Callback for video control events (mute, play) during ad playback. |
persistent | boolean | Show the ad each time it activates, ignoring the "already viewed" check. |
VideoPlayerCallback
type VideoPlayerCallback = (data: { muted?: boolean; play?: boolean }) => void
Register this callback to react to SDK-initiated video control events. For example, when an external VAST ad plays, the SDK may request muting the host video.
How It Works
- Promotion activates — the SDK renders the ad layout off-screen to measure the target slot dimensions.
- Style capture — the SDK snapshots the video element's current inline styles so they can be restored later.
- Animated transition — the video element receives
position: absoluteand is smoothly transitioned (750ms) into the left half of the layout while the ad content fades in on the right. - Live tracking — a
ResizeObserverwatches the layout slot, keeping the video coordinates up-to-date on window or container resize. - Ad closes — the video transitions back to its original size and position, and the saved inline styles are restored.
Note: The SDK temporarily overrides the video element's inline styles while the ad is visible. Avoid setting the same style properties on the video element during this time.
Responsive Layout
The SDK automatically adapts the side-by-side layout based on the container width:
| Container Width | Layout | Behavior |
|---|---|---|
| ≥ 576px | Desktop | 50/50 split — video on left, ad content on right, with header and footer. |
| < 576px | Mobile | Overlay — ad appears as an overlay on top of the video instead of the split layout. |
The breakpoint is calculated from the SDK container width, not the viewport.
mobileBreakpoint
The mobileBreakpoint prop on StreamLayerProvider controls when the SDK switches from the 50/50 split to the overlay layout. The default is 576 pixels — this works well for most integrations and typically does not need to be changed.
The overlay activates when the container width is below this value and the device is in portrait orientation.
<StreamLayerProvider
sdkKey="your-sdk-key"
event="your-event-id"
mobileBreakpoint={1024}
>
<StreamLayerSDKAdvertisement
sideBySide
videoRef={videoContainerRef}
/>
</StreamLayerProvider>
Note: Only override
mobileBreakpointif you need the overlay layout to appear at a wider container size than the default576px. In most cases, the default is sufficient.
Implementation Example
import { useCallback, useRef, useState } from 'react'
import { StreamLayerProvider, type VideoPlayerCallback } from '@streamlayer/react'
import { StreamLayerSDKAdvertisement } from '@streamlayer/react/advertisement'
export const VideoView = () => {
const videoRef = useRef<HTMLVideoElement>(null)
const videoContainerRef = useRef<HTMLDivElement>(null)
const [muted, setMuted] = useState(false)
// Called when SDK requests video control (e.g., mute during external ad)
const videoPlayerController: VideoPlayerCallback = useCallback((data) => {
if (!videoRef.current) return
if (data.muted !== undefined) {
videoRef.current.muted = data.muted
setMuted(data.muted)
}
if (data.play === true) {
videoRef.current.play()
}
}, [])
return (
<div style={{ position: 'relative' }}>
<div ref={videoContainerRef}>
<video
ref={videoRef}
src="https://example.com/stream.m3u8"
autoPlay
controls
/>
</div>
<StreamLayerProvider
sdkKey="your-sdk-key"
event="your-event-id"
>
<StreamLayerSDKAdvertisement
sideBySide
externalAd
videoRef={videoContainerRef}
videoPlayerController={videoPlayerController}
muted={muted}
/>
</StreamLayerProvider>
</div>
)
}
Key Points
videoRefis required: Pass a ref to your video container element. The video's parent must haveposition: relative.- Enable
externalAdfor VAST ads: If your promotions include external VAST/GAM video ads, passexternalAdalongsidesideBySide. - Register
videoPlayerController: Implement this callback to respond to SDK-initiated mute/play requests during ad playback. - Responsive by default: The SDK automatically switches between the 50/50 split and overlay based on container width. The default
mobileBreakpointof576pxworks for most cases. - Don't fight inline styles: The SDK temporarily overrides video element styles during ad playback. Avoid modifying the same properties while an ad is active.
Fullscreen: Use Fake Fullscreen
The browser's native Fullscreen API (element.requestFullscreen()) creates a new top-level stacking context that is isolated from the rest of the document. StreamLayer overlays cannot render inside a native fullscreen element — the ad panel, controls, and any other SDK UI will be hidden behind the fullscreen layer.
Instead, implement fake fullscreen — a CSS-based approach that makes the video container fill the entire viewport without leaving the normal DOM flow:
.video-container--fullscreen {
position: fixed;
top: 0;
left: 0;
width: 100vw;
height: 100vh;
z-index: 9999;
background: #000;
}
const [isFullscreen, setIsFullscreen] = useState(false)
const toggleFullscreen = () => setIsFullscreen((prev) => !prev)
return (
<div
className={isFullscreen ? 'video-container--fullscreen' : 'video-container'}
style={{ position: 'relative' }}
>
<div ref={videoContainerRef}>
<video ref={videoRef} src="https://example.com/stream.m3u8" autoPlay controls />
<button onClick={toggleFullscreen}>
{isFullscreen ? 'Exit Fullscreen' : 'Fullscreen'}
</button>
</div>
<StreamLayerProvider sdkKey="your-sdk-key" event="your-event-id">
<StreamLayerSDKAdvertisement
sideBySide
videoRef={videoContainerRef}
/>
</StreamLayerProvider>
</div>
)
Because the SDK components remain siblings of the video container inside the same DOM tree, the side-by-side layout, overlays, and all other SDK UI continue to work correctly in fake fullscreen.
Warning: If your app already uses
requestFullscreen(), you must replace it with the CSS approach above. There is no workaround — the native Fullscreen API is fundamentally incompatible with external overlays rendered outside the fullscreen element.
Muting the Host Video During Ad Playback
When a side-by-side ad includes audio (e.g., an external VAST video ad), two audio sources play simultaneously — the host video stream and the ad. The SDK uses the videoPlayerController callback to request that the host video is muted while the ad is playing and unmuted when the ad ends.
How it works
- The SDK calls your
videoPlayerControllerwith{ muted: true }when an ad with audio starts playing. - When the ad ends, pauses, or is dismissed, the SDK calls it again with
{ muted: false }. - Your callback is responsible for actually muting the host
<video>element (or your third-party player).
Basic implementation
const videoPlayerController: VideoPlayerCallback = useCallback((data) => {
if (!videoRef.current) return
if (data.muted !== undefined) {
videoRef.current.muted = data.muted
}
}, [])
Implementation with volume reduction
If you prefer to reduce volume instead of fully muting, you can lower the volume and restore it when the ad finishes:
const savedVolume = useRef(1)
const videoPlayerController: VideoPlayerCallback = useCallback((data) => {
if (!videoRef.current) return
if (data.muted === true) {
savedVolume.current = videoRef.current.volume
videoRef.current.volume = 0.1 // reduce to 10%
} else if (data.muted === false) {
videoRef.current.volume = savedVolume.current
}
}, [])
Third-party player integration
For players like Video.js, hls.js, or custom wrappers, adapt the callback to use your player's API:
// Video.js example
const videoPlayerController: VideoPlayerCallback = useCallback((data) => {
if (!playerRef.current) return
if (data.muted !== undefined) {
playerRef.current.muted(data.muted)
}
}, [])
Important: Always pass the
videoPlayerControllercallback alongsideexternalAdwhen using external VAST ads. Without it, the host video and the ad will play audio simultaneously, resulting in a poor user experience.
Related
- Advertising — Advertisement component integration
- Exposed Pause Ad — Pause ad overlay integration
- Video Controller — Volume control during SDK events
- Integration Guide — Full Web SDK setup and configuration