Play live streams
Play live streams with live-edge tracking, DVR windows, and stream-type detection.
Play live streams: show a live badge, jump to the live edge, and support DVR windows when the stream allows scrubbing back.
Recommended approach
Use the live feature bundle with a streaming media element, and give users a LiveButton: it shows whether playback is at the live edge and seeks back to it when activated.
import { Container, LiveButton } from '@videojs/react';
import { LiveVideoPlayer } from '@videojs/react/live-video';
import { HlsJsVideo } from '@videojs/react/media/hlsjs-video';
export default function App() {
return (
<LiveVideoPlayer>
<Container>
<HlsJsVideo src="https://your-stream.example.com/live.m3u8" autoPlay muted playsInline />
<LiveButton render={(props, state) => <button {...props}>{state.liveEdge ? 'LIVE' : 'Go live'}</button>} />
</Container>
</LiveVideoPlayer>
);
}<live-video-player>
<media-container>
<hlsjs-video src="https://your-stream.example.com/live.m3u8" autoplay muted playsinline></hlsjs-video>
<media-live-button></media-live-button>
</media-container>
</live-video-player>
<script type="module">
import '@videojs/html/live-video/player';
import '@videojs/html/media/hlsjs-video';
import '@videojs/html/live-video/ui';
</script>The liveVideoFeatures bundle mirrors videoFeatures but adds the live feature and drops playback rate (not meaningful for live), quality selection, and audio-track selection. The live presets also ship skins without duration and current-time displays.
Both bundles are plain arrays, so extending one is a spread:
import { createPlayer, streamTypeFeature } from '@videojs/react';
import { liveVideoFeatures } from '@videojs/react/live-video';
const { Player } = createPlayer({ features: [...liveVideoFeatures, streamTypeFeature] });import { createPlayer, streamTypeFeature } from '@videojs/html';
import { liveVideoFeatures } from '@videojs/html/live-video';
const { ProviderMixin } = createPlayer({ features: [...liveVideoFeatures, streamTypeFeature] });How it works
Two features describe liveness:
- The stream type feature reports
streamType:'live','on-demand', or'unknown'. Streaming media derives it from manifest metadata; plain media elements fall back to duration-based detection.liveVideoFeaturesdoes not include it; addstreamTypeFeatureto the player’s features when you needstreamType. - The live feature reports two values:
liveEdgeStartis where “live” begins on the timeline. When the playhead is at or past this time, the viewer is watching live; behind it, they’re watching earlier moments of the stream.targetLiveWindowis how far back the stream lets viewers rewind.0means a plain live stream that stays at the edge.Infinitymeans full DVR: rewind as far as the recording goes.NaNmeans the stream isn’t live, or the player doesn’t know yet.
The time feature adjusts for live playback: duration reports the live edge (the end of the seekable range) and keeps growing as the stream continues.
LiveButton reads this state: it renders as an active “go to live” control while playback is behind the edge, seeks to the edge on activation, and goes inactive at the edge.
Availability and constraints
liveEdgeStartandtargetLiveWindowcome from media that implements the live capability — the hls.js-based media elements. With plain media elements, stream type falls back to duration-based detection andliveEdgeStartstaysNaN.- Live playback drifts behind the edge when the network stalls or the tab is backgrounded. Always give users a path back to the edge.
- Seeking makes sense only for DVR streams (
targetLiveWindowofInfinity). For standard-latency streams, omit the time slider. - Playback rate control isn’t meaningful for live;
liveVideoFeaturesomits it.
Common variations
DVR: let users scrub back
For streams with a DVR window, keep a TimeSlider in the UI. The slider tracks the sliding window automatically because duration follows the live edge.
Switch UI by stream type
Render live or on-demand controls from streamType when one player handles both kinds of content. Add streamTypeFeature to the player’s features; liveVideoFeatures does not include it:
The preset LiveVideoPlayer comes with a fixed feature set. To change it, build the player yourself with createPlayer — the escape hatch for custom feature sets:
import { Container, createPlayer, LiveButton, streamTypeFeature } from '@videojs/react';
import { liveVideoFeatures } from '@videojs/react/live-video';
import { HlsJsVideo } from '@videojs/react/media/hlsjs-video';
const { Player, usePlayer } = createPlayer({ features: [...liveVideoFeatures, streamTypeFeature] });
function LiveBadge() {
const streamType = usePlayer((s) => s.streamType);
if (streamType !== 'live') return null;
return <LiveButton render={(props, state) => <button {...props}>{state.liveEdge ? 'LIVE' : 'Go live'}</button>} />;
}
export default function App() {
return (
<Player>
<Container>
<HlsJsVideo src="https://your-stream.example.com/live.m3u8" autoPlay muted playsInline />
<LiveBadge />
</Container>
</Player>
);
}<media-live-button> selects from the live, time, and buffer features and reflects live state. To swap larger parts of the UI on streamType, build a provider with createPlayer and add streamTypeFeature to its features (<live-video-player> uses liveVideoFeatures, which does not include it), then read streamType from the player store through the player controller.
Troubleshooting
The live badge never activates
streamType isn’t 'live'. Confirm the player’s features include streamTypeFeature (liveVideoFeatures does not include it, so streamType reads as undefined), the source is a live manifest, and it plays through a live-capable media element such as HlsJsVideo.
Playback keeps falling behind the edge
Network throughput can’t sustain the stream, or the tab was backgrounded. LiveButton returns users to the edge; for lower drift, review the stream’s latency configuration on the encoder side.
The time slider behaves oddly on a live stream
Standard-latency live streams have no meaningful seek range. Show the slider only for DVR streams (targetLiveWindow of Infinity).