Split Components

Use StreamLayerSDKTvContent and StreamLayerSDKTvPauseAd for full control over SDK UI element placement. Place sidebars, notifications, banners, and pause ads independently within your own layout instead of relying on the all-in-one StreamLayerSDKTv wrapper.

Overview

By default, the Integration Guide recommends wrapping your video element with StreamLayerSDKTv. This all-in-one component manages sidebar, notification, banner, and pause ad rendering automatically.

Safe to wrap your video: StreamLayerSDKTv wraps your video element, which may raise concerns about affecting video rendering. The SDK uses internal fallbacks — if anything goes wrong, your children (video) are always rendered. The SDK will never prevent your video from displaying.

However, if you need fine-grained control over where each UI element is rendered — for example, placing the sidebar outside the video container or customizing notification positions — you can use the split components instead:

ComponentPurpose
StreamLayerSDKTvContentRenders sidebars, notifications, or banners
StreamLayerSDKTvPauseAdRenders the pause ad overlay

Both components must be rendered inside a StreamLayerProvider.

Important: Split components do not include any positioning styles by default. If you place them without CSS, nothing will appear on screen. You must define positioning styles yourself using the SDK's internal CSS class names.


When to Use Split Components

Use CaseRecommended Approach
Standard integration, minimal customizationStreamLayerSDKTv
Custom layout for sidebar / notification / bannerSplit components
Pause ad managed separately from other SDK featuresSplit components
Different positioning per UI element typeSplit components

Components

StreamLayerSDKTvContent

Renders interactive SDK content based on the current UI state. Use the notification and banner props to control which type of content is rendered. Without either prop, it renders sidebar content (promotions and questions).

type StreamLayerSDKTvContentProps = {
  persistent?: boolean    // Persist UI state across re-renders
  notification?: boolean  // Render notification content
  banner?: boolean        // Render banner content
}

To render all three types, use three separate instances:

<StreamLayerSDKTvContent />              {/* Sidebar */}
<StreamLayerSDKTvContent notification /> {/* Notifications */}
<StreamLayerSDKTvContent banner />       {/* Banners */}

Each instance only renders when the SDK has matching content to display.

StreamLayerSDKTvPauseAd

Renders the pause ad overlay independently. Accepts the same props as the pause ad configuration on StreamLayerSDKTv.

type StreamLayerSDKTvPauseAdProps = {
  showPauseAd?: boolean
  onRender?: (params: { rendered: boolean }) => void
  onClose?: () => void
  videoPlayerController: VideoPlayerCallback
  options?: StreamLayerSDKTvOptions
  vastUrls?: Array<{ template?: 'default'; url: string }>
}

For detailed documentation on pause ad props and state management (showPauseAd vs pauseAdRendered), see Exposed Pause Ad.


Required CSS Styling

Split components render without positioning. You must add CSS for the internal class names to position elements correctly within your layout.

CSS Class Names

Class NameElement
.SL-AdvertisementWebOs--sidebarSidebar panel
.SL-AdvertisementWebOs--notificationNotification
.SL-AdvertisementWebOs--bannerBanner
.SL_PauseAdPause ad

Minimal Positioning Styles

The styles below are the minimum required to position SDK elements on screen. Without them, split components will render in the DOM but will not be visible. Apply these in your global CSS or a styled-component wrapping the video container:

/* Sidebar – anchored to the right edge */
.SL-AdvertisementWebOs--sidebar {
  position: absolute;
  width: 350px;
  right: 0;
  top: 0;
  bottom: 0;
}

/* Notification – anchored to the bottom-left */
.SL-AdvertisementWebOs--notification {
  position: absolute;
  left: 0;
  bottom: 0;
  top: auto;
  z-index: 1000;
}

/* Banner – bottom bar, leaving room for the sidebar */
.SL-AdvertisementWebOs--banner {
  position: absolute;
  left: 10px;
  right: 360px;   /* 350px sidebar + 10px gap */
  bottom: 10px;
  top: auto;
  z-index: 1000;
  height: 90px;
}

/* Pause Ad – right-aligned overlay */
.SL_PauseAd {
  width: 300px !important;
  left: auto !important;
}

Adjust values to match your app's layout. For example, the right: 360px on the banner accounts for a 350px sidebar plus a 10px gap — update accordingly if you change the sidebar width.

Discovering Additional Class Names

You can inspect SDK elements in your browser's developer tools to discover additional internal class names. All rendered SDK elements can be further styled using regular CSS targeting these class names. This gives you full flexibility to customize the look and positioning beyond the minimal styles above.


useStreamLayerUI Hook

The useStreamLayerUI hook exposes boolean flags that reflect the current SDK UI state. With split components, you can use these flags to control when and where each element renders — giving you full ownership of the rendering logic.

import { useStreamLayerUI } from '@streamlayer/web-os'

Available Flags

FlagDescription
promotionSidebarA promotion sidebar is ready to display
promotionNotificationA promotion notification is ready to display
promotionBannerA promotion banner is ready to display
promotionOverlayA promotion overlay is ready to display
promotionExternalAdAn external ad promotion is ready to display
appAn interactive app (poll, trivia, etc.) is active
appSidebarAn app sidebar is ready to display
appBannerAn app banner is ready to display
appNotificationAn app notification is ready to display
onboardingNotificationAn onboarding notification is ready to display
exposedPauseAdA pause ad is currently exposed

Conditional Rendering Example

Each StreamLayerSDKTvContent instance already handles its own rendering internally — it only displays content when the SDK has matching data. You do not need to conditionally render these components yourself for them to work correctly.

However, combining useStreamLayerUI flags with conditional rendering gives you greater customization control. For example, you can:

  • Adjust your layout dynamically — resize the video area when a sidebar appears, or shift controls when a banner is active
  • Add custom animations or transitions when SDK elements mount or unmount
  • Introduce delays before showing or hiding elements
  • Completely remove component containers from the DOM when not in use, giving you full control over your page structure
import { useStreamLayerUI, StreamLayerSDKTvContent } from '@streamlayer/web-os'

const SDKOverlays = () => {
  const uiState = useStreamLayerUI()

  const hasSidebar = uiState.promotionSidebar || uiState.app || uiState.appSidebar
  const hasNotification = uiState.promotionNotification || uiState.onboardingNotification || uiState.appNotification
  const hasBanner = uiState.promotionBanner || uiState.appBanner

  return (
    <>
      {/* Conditionally render sidebar — e.g., to adjust video area width */}
      {hasSidebar && (
        <StreamLayerSDKTvContent />
      )}

      {/* Conditionally render notification — e.g., to add a mount animation */}
      {hasNotification && (
        <StreamLayerSDKTvContent notification />
      )}

      {/* Conditionally render banner — e.g., to shift controls when active */}
      {hasBanner && (
        <StreamLayerSDKTvContent banner />
      )}
    </>
  )
}

This pattern is entirely optional — all three StreamLayerSDKTvContent instances can be rendered unconditionally and will work correctly. Use this approach only when you need additional control over your layout or rendering behavior.

For more details on the UI context hook, see Using StreamLayer UI Context.


Full Integration Example

import { useState, useCallback, useRef } from 'react'
import {
  StreamLayerProvider,
  StreamLayerSDKTvContent,
  StreamLayerSDKTvPauseAd,
  type VideoPlayerCallback,
} from '@streamlayer/web-os'
import '@streamlayer/web-os/style.css'

export const VideoSplitView = () => {
  const [showPauseAd, setShowPauseAd] = useState(false)
  const [pauseAdRendered, setPauseAdRendered] = useState(false)
  const videoRef = useRef<HTMLVideoElement>(null)

  const onClosePauseAd = useCallback(() => {
    setShowPauseAd(false)
  }, [])

  const onRenderPauseAd = useCallback((params: { rendered: boolean }) => {
    setPauseAdRendered(params.rendered)
  }, [])

  const videoPlayerController: VideoPlayerCallback = useCallback((data) => {
    if (data.play === true) {
      setShowPauseAd(false)
      videoRef.current?.play()
    }
  }, [])

  const onVideoPlay = useCallback(() => {
    setShowPauseAd(false)
  }, [])

  const onVideoPause = useCallback(() => {
    setShowPauseAd(true)
  }, [])

  // Always use pauseAdRendered (not showPauseAd) to control UI visibility
  const showControls = !pauseAdRendered

  return (
    <StreamLayerProvider sdkKey="your-sdk-key" event="your-event-id">
      <div style={{ position: 'relative', width: '100%', height: '100%' }}>
        {/* Video element */}
        <video
          ref={videoRef}
          onPause={onVideoPause}
          onPlay={onVideoPlay}
          style={{ width: '100%', height: '100%' }}
        />

        {/* Your custom controls – hidden when pause ad is rendered */}
        {showControls && (
          <div className="video-controls">
            {/* Play button, timeline, volume, etc. */}
          </div>
        )}

        {/* Split SDK components – place anywhere inside the container */}
        <StreamLayerSDKTvContent />
        <StreamLayerSDKTvContent notification />
        <StreamLayerSDKTvContent banner />

        <StreamLayerSDKTvPauseAd
          showPauseAd={showPauseAd}
          onRender={onRenderPauseAd}
          onClose={onClosePauseAd}
          videoPlayerController={videoPlayerController}
          options={{ showPauseButton: false }}
          vastUrls={[
            {
              template: 'default',
              url: 'https://your-vast-tag-url.com/vast.xml',
            },
          ]}
        />
      </div>
    </StreamLayerProvider>
  )
}

Add the required positioning styles (see Required CSS Styling above) to your app's stylesheet or a styled wrapper around the video container.


Key Points

  1. Wrapping video is safe: StreamLayerSDKTv uses fallbacks internally — your children (video) are always rendered even if the SDK encounters an error.
  2. Three content instances: Use separate StreamLayerSDKTvContent components for sidebar, notification, and banner — each with the appropriate prop.
  3. CSS is required: Without positioning styles on the SDK's internal class names, elements will not be visible. Use browser dev tools to inspect rendered elements and discover class names for further styling.
  4. Container must be position: relative: The split components use position: absolute, so their parent container needs relative positioning.
  5. Customize rendering with useStreamLayerUI: Each StreamLayerSDKTvContent instance handles its own rendering internally. Use the hook's boolean flags for additional customization — adjusting your layout, adding animations, or controlling when component containers are in the DOM.
  6. Pause ad state: Use pauseAdRendered (from onRender callback) — not showPauseAd — to control your UI visibility. See Exposed Pause Ad for details.
  7. Single provider: All split components must live inside one StreamLayerProvider. Do not create multiple providers.

Related Documentation