Skip to main content
Overlays are full-screen views layered on top of the video player in each feed cell. They read state from ShortKitPlayer reactive streams and issue commands back to the player. The SDK manages the overlay lifecycle — creating instances per cell, configuring them with content, and coordinating transitions as the user swipes between videos.

Video overlay modes

VideoOverlayMode controls what UI the SDK renders on top of video content. Set it via FeedConfig:

Custom video overlays

When the built-in template doesn’t fit your product — custom controls, branded UI, domain-specific actions — use .custom to provide your own overlay. The SDK manages per-cell creation, content binding, and transition lifecycle exactly as it does for built-in templates. You own the UI; the SDK owns the lifecycle.

The FeedOverlay protocol

Custom overlays must conform to FeedOverlay. The SDK calls these methods at specific moments in the cell lifecycle:
The SDK calls your factory closure once per cell, lazily on first use. Your overlay is added as a full-screen subview of the cell and persists across cell reuses. Immediately after creation, the SDK calls attach(player:) with its ShortKitPlayer instance — this is where you store the player reference and subscribe to its reactive streams. From that point on, the SDK drives your overlay through the remaining protocol methods. When a cell is reused for new content, it calls configure(with:) followed by resetPlaybackProgress() so your overlay can update its metadata and clear stale state. When the cell becomes the active playback cell, it calls activatePlayback() — your signal to start processing time updates.

Registering your overlay

Pass a factory closure to .custom. The closure returns a view conforming to FeedOverlay. The SDK calls attach(player:) immediately after to provide the player:
The factory is your opportunity to inject your own dependencies — themes, data services, feature flags — at construction time. On iOS and Android, the SDK provides its dependency (the player) separately through attach(player:). In React Native, your component accesses the player via the useShortKitPlayer() hook.

SwiftUI overlays

If your iOS app uses SwiftUI, use .swiftUI instead of .custom to build your overlay as a SwiftUI View. The closure receives a ShortKitPlayer and a CellContent object:
CellContent is an ObservableObject with a single @Published item: ContentItem? property. The SDK updates it automatically as cells are reused. Use it in your SwiftUI view to reactively display content metadata:
Under the hood, .swiftUI wraps your view in a UIHostingController and registers it as a FeedOverlay. You do not need to conform to the FeedOverlay protocol yourself.

ShortKitPlayer

ShortKitPlayer is the public interface to the video player. Both built-in templates and custom overlays use it the same way: subscribe to reactive streams for state, call methods for commands. Access it via shortKit.player.

Reactive streams

Subscribe to these streams to keep your overlay in sync with playback state. Updates are delivered on the main thread. Synchronous reads are available without subscribing via currentItemValue and playbackRateValue.

Commands

Call these methods from your gesture handlers and buttons to control playback:

ContentItem

The content metadata available to your overlay via configure(with:) and the currentItem publisher:

Example: scrubber with seek thumbnails

This example shows a draggable scrubber that displays a thumbnail preview at the seek position while the user drags. It uses player.time to track progress and player.seekThumbnail(at:) to fetch storyboard frames.
The key pattern: guard on isActive and !isScrubbing in your time stream handler so you only update the scrubber from playback when the overlay is active and the user isn’t dragging. During a drag, update the UI from the gesture position instead and call player.seekAndPlay(to:) on release.

Feed scroll control

By default the feed owns vertical swipe: dragging up or down navigates between items. When a custom overlay has its own drag gesture — a horizontal scrubber, a slider, a draggable control — the feed’s swipe can capture the touch mid-drag. Toggle feed scroll to give your gesture exclusive priority while it is in flight: disable it when the gesture starts, re-enable it when the gesture ends or is cancelled.
Call it on the player:
Call it as a matched pair driven by your gesture lifecycle: disable when the gesture starts, re-enable when it ends or is cancelled.
This is a transient handoff for an in-flight gesture, not a persistent feed lock. The toggle stays where you set it: if you disable scroll and never re-enable it, vertical swipe stays blocked. Always pair a disable call with a matching re-enable.

Example: closed captions

This example shows a caption label that follows the active cue and can be repositioned by dragging. It uses player.activeCue for the text and a pan gesture for repositioning.
The activeCue publisher emits a VTTCue (with startTime, endTime, and text) when a cue becomes active, and nil when no cue covers the current playback time. The SDK handles cue timing internally — your overlay just renders whatever text it receives.

Engagement signals

When your custom overlay includes actions like likes, shares, or saves, report them to the SDK using sendContentSignal. This feeds the recommendation engine so future feed content reflects user preferences.
Custom overlays handle their own user interaction callbacks and report engagement through content signals. Image carousels, video carousels, surveys, and native ads follow the same overlay pattern as video content. Each has a mode enum with .none and .custom { ... }. The relationship is the same: the SDK owns the cell lifecycle, and the overlay provides the UI.
Custom carousel overlays conform to CarouselOverlay. Unlike video overlays, there is no player — the overlay owns everything: image display, auto-scroll, text, and actions. The SDK provides carousel data through configure(with:) and manages cell lifecycle.
The ImageCarouselItem provided in configure(with:) contains the carousel’s images, metadata, and optional auto-scroll interval. Your overlay is responsible for displaying the images and handling all user interaction — paging, text expansion, action buttons — directly. Video carousel overlays conform to VideoCarouselOverlay. Unlike image carousel overlays, the SDK handles horizontal video paging and playback natively — your overlay only provides the chrome layer (titles, buttons, page indicators) rendered on top.
In React Native, video carousel overlays are React components that receive props directly:
Register your component in the feed config:
Your component re-renders when the active video changes (horizontal swipe), when the cell becomes active/inactive (vertical swipe), and when player state updates. Use activeVideoIndex to render page indicators and carouselItem for carousel-level metadata. To observe or control the active carousel from your overlay or host app — navigate between videos programmatically, read the active index, or react to video completions — see Video carousel.

Per-feed overlay binding (React Native)

When you mount more than one <ShortKitFeed> at the same time — for example one feed per tab in a swipeable tab layout — each feed’s overlay cells need to know which feed they belong to, so they render that feed’s own data instead of whichever feed is currently focused. Give each mounted feed a stable, unique feedKey:
Every overlay component that feed renders then receives a matching feedId prop, equal to that feed’s feedKey:
feedId is present on the props of every overlay component — video, image-carousel, and video-carousel overlays. When you don’t supply a feedKey, the SDK generates a random feed id and overlays receive that, which is all the single-feed case needs. Pass an explicit feedKey only when multiple feeds are mounted at once and overlays must be attributed to a specific feed.
At its first or last page, the video carousel releases a horizontal pan instead of consuming it, so the gesture can flow to whatever scroll container wraps the feed. This is what makes edge-swipe tab navigation possible — but it is not automatic on its own. You have to host the feed inside a swipe-enabled pager (e.g. a tab navigator with swipe gestures enabled), and whether an edge swipe actually advances a tab is determined by that pager’s own gesture handling, which you configure on the host side. The SDK’s only role is to stop the carousel from absorbing the pan at its edges.

Custom survey overlays

Custom survey overlays conform to SurveyOverlay. The protocol includes two callback properties that the SDK sets on your overlay after creation. Your overlay calls these when the user selects a survey option — the SDK handles forwarding the response to the host app delegate and triggering auto-advance.

Custom ad overlays

Custom ad overlays conform to AdOverlay. They apply to native ads only — ads where the video plays through ShortKit’s own PlayerPool and the SDK provides creative metadata via NativeAdContent. System-defined ads (IMA/VAST) render their own UI through the ad provider and are unaffected by this setting.
NativeAdContent provides all creative assets and action handlers:
Ad policy compliance. When building a custom ad overlay, you are responsible for rendering the required policy elements: an “Ad” badge and a tappable AdChoices icon (minimum 15x15pt). Verify your implementation meets your ad provider’s display requirements.