@im03/react-hls-player
v0.2.0
Published
A lightweight, TypeScript-first React component for playing HLS (HTTP Live Streaming) video streams. Built on top of [hls.js](https://github.com/video-dev/hls.js) with full support for React 18 and 19.
Maintainers
Readme
@im03/react-hls-player
A lightweight, TypeScript-first React component for playing HLS (HTTP Live Streaming) video streams. Built on top of hls.js with full support for React 18 and 19.
Features
- Native HLS playback via hls.js with automatic fallback for browsers that support HLS natively (Safari)
- Written in TypeScript with full type exports
- Configurable hls.js options via
hlsConfigprop - Built-in
presetsystem for tuning buffer behavior (performance,balanced,quality) - Automatic error recovery with exponential backoff retry for network errors
- Imperative handle ref (
ReactHlsPlayerRef) for programmatic control: play, pause, seek, volume, quality level - Supports
playerReffor direct access to the underlying<video>element - Callback props for player lifecycle events:
onReady,onPlay,onPause,onEnded,onError,onLevelsLoaded - Compatible with React 18 and 19
- Zero wrapper overhead — renders a plain
<video>element
Installation
npm install @im03/react-hls-player hls.jsyarn add @im03/react-hls-player hls.jspnpm add @im03/react-hls-player hls.jsPeer dependencies: This package requires
reactandreact-dom(v18 or v19) andhls.jsto be installed in your project.
Basic Usage
import { ReactHlsPlayer } from "@im03/react-hls-player";
export default function App() {
return (
<ReactHlsPlayer
src="http://content.jwplatform.com/manifests/vM7nH0Kl.m3u8"
autoPlay={false}
muted={false}
/>
);
}Props
| Prop | Type | Default | Description |
| ----------------- | --------------------------------------------- | ------------ | --------------------------------------------------------------------------------------------- |
| src | string | Required | The URL of the HLS stream (.m3u8 manifest). |
| playerRef | RefObject<HTMLVideoElement> | — | Ref to the underlying <video> element. Use this for programmatic control. |
| hlsConfig | HlsConfig | {} | Partial hls.js configuration object. See HlsConfig for available options. |
| preset | "performance" \| "balanced" \| "quality" | "balanced" | Buffer preset. See Presets for details. |
| autoPlay | boolean | false | Whether to start playback automatically. May be blocked by browsers without user interaction. |
| muted | boolean | true | Whether the video is muted. Set to false to enable audio. |
| poster | string | undefined | URL of a poster image shown before the video plays. |
| className | string | "" | Additional CSS class name(s). The element always includes react-hls-player as a base class. |
| onReady | () => void | — | Called when the manifest is parsed and the player is ready. |
| onPlay | () => void | — | Called when playback starts. |
| onPause | () => void | — | Called when playback is paused. |
| onEnded | () => void | — | Called when the video ends. |
| onError | (error: unknown) => void | — | Called on any hls.js error (fatal or not). |
| onLevelsLoaded | (levels: string[]) => void | — | Called with a list of human-readable resolution labels (e.g. ["360p", "720p", "1080p"]) when the manifest is parsed. |
| ...props | React.VideoHTMLAttributes<HTMLVideoElement> | — | Any valid HTML <video> attribute is forwarded to the underlying element. |
Presets
The preset prop provides opinionated buffer configuration shortcuts. You can still override any value via hlsConfig.
| Preset | Description |
| ------------- | --------------------------------------------------------------------------- |
| performance | Small buffer (maxBufferLength: 10, maxMaxBufferLength: 20). Best for low-memory or mobile environments. |
| balanced | hls.js defaults. Suitable for most use cases. |
| quality | Large buffer (maxBufferLength: 60, maxMaxBufferLength: 120). Best for VOD where smooth, uninterrupted playback is preferred. |
<ReactHlsPlayer
src="https://example.com/stream.m3u8"
preset="quality"
muted={true}
/>Imperative Handle (ReactHlsPlayerRef)
Pass a ref directly to <ReactHlsPlayer> to get access to imperative controls. This is separate from playerRef (which gives access to the raw <video> element).
interface ReactHlsPlayerRef {
play: () => void;
pause: () => void;
seek: (time: number) => void;
setVolume: (volume: number) => void; // 0.0 – 1.0
setQuality: (level: number) => void; // hls.js level index; -1 for auto
}import { useRef } from "react";
import { ReactHlsPlayer, ReactHlsPlayerRef } from "@im03/react-hls-player";
export default function Player() {
const controlRef = useRef<ReactHlsPlayerRef>(null);
return (
<>
<ReactHlsPlayer
ref={controlRef}
src="https://example.com/stream.m3u8"
muted={true}
/>
<button onClick={() => controlRef.current?.play()}>Play</button>
<button onClick={() => controlRef.current?.pause()}>Pause</button>
<button onClick={() => controlRef.current?.seek(30)}>Skip to 0:30</button>
<button onClick={() => controlRef.current?.setVolume(0.5)}>50% Volume</button>
<button onClick={() => controlRef.current?.setQuality(0)}>Lowest Quality</button>
</>
);
}HlsConfig
The hlsConfig prop accepts a partial configuration object for hls.js. All fields are optional.
type HlsConfig = Partial<{
autoStartLoad: boolean;
startPosition: number;
debug: boolean;
enableWorker: boolean;
lowLatencyMode: boolean;
maxBufferLength: number;
maxMaxBufferLength: number;
maxBufferSize: number;
capLevelToPlayerSize: boolean;
abrEwmaFastLive: number;
abrEwmaSlowLive: number;
abrEwmaFastVoD: number;
abrEwmaSlowVoD: number;
enableCEA708Captions: boolean;
enableWebVTT: boolean;
}>;For the full list of hls.js configuration options, refer to the hls.js API documentation.
Examples
Autoplay with sound
Note that most browsers block autoplay with audio unless the user has interacted with the page. If autoplay is blocked, a warning is logged to the console instead of throwing an error.
<ReactHlsPlayer
src="https://example.com/stream.m3u8"
autoPlay={true}
muted={false}
/>With a poster image
<ReactHlsPlayer
src="http://content.jwplatform.com/manifests/vM7nH0Kl.m3u8"
poster="https://upload.wikimedia.org/wikipedia/commons/7/7c/Aspect_ratio_16_9_example.jpg"
muted={true}
/>Quality switcher using onLevelsLoaded
onLevelsLoaded receives a list of resolution labels derived from the manifest, such as ["360p", "720p", "1080p"]. Use this alongside setQuality on the imperative ref to build a quality selector.
import { useRef, useState } from "react";
import { ReactHlsPlayer, ReactHlsPlayerRef } from "@im03/react-hls-player";
export default function Player() {
const controlRef = useRef<ReactHlsPlayerRef>(null);
const [levels, setLevels] = useState<string[]>([]);
return (
<>
<ReactHlsPlayer
ref={controlRef}
src="https://example.com/stream.m3u8"
muted={true}
onLevelsLoaded={setLevels}
/>
<select onChange={(e) => controlRef.current?.setQuality(Number(e.target.value))}>
<option value={-1}>Auto</option>
{levels.map((label, i) => (
<option key={i} value={i}>{label}</option>
))}
</select>
</>
);
}Lifecycle callbacks
<ReactHlsPlayer
src="https://example.com/stream.m3u8"
muted={true}
onReady={() => console.log("Player ready")}
onPlay={() => console.log("Playing")}
onPause={() => console.log("Paused")}
onEnded={() => console.log("Ended")}
onError={(err) => console.error("HLS error", err)}
onLevelsLoaded={(levels) => console.log("Available qualities:", levels)}
/>Controlling the player via playerRef
Pass a ref via playerRef to access the native HTMLVideoElement directly.
import { useRef } from "react";
import { ReactHlsPlayer } from "@im03/react-hls-player";
export default function Player() {
const playerRef = useRef<HTMLVideoElement>(null);
return (
<>
<ReactHlsPlayer
src="https://example.com/stream.m3u8"
playerRef={playerRef}
muted={true}
/>
<button onClick={() => playerRef.current?.play()}>Play</button>
<button onClick={() => playerRef.current?.pause()}>Pause</button>
</>
);
}Custom hls.js configuration
<ReactHlsPlayer
src="https://example.com/stream.m3u8"
hlsConfig={{
lowLatencyMode: true,
maxBufferLength: 30,
capLevelToPlayerSize: true,
debug: false,
}}
muted={true}
/>Low-latency live stream
<ReactHlsPlayer
src="https://example.com/live.m3u8"
autoPlay={true}
muted={true}
preset="performance"
hlsConfig={{
lowLatencyMode: true,
enableWorker: true,
abrEwmaFastLive: 3,
abrEwmaSlowLive: 9,
}}
/>Forwarding native video attributes
Any standard HTML <video> attribute not listed in the props table is forwarded directly to the video element.
<ReactHlsPlayer
src="https://example.com/stream.m3u8"
muted={true}
width={1280}
height={720}
style={{ borderRadius: "8px" }}
aria-label="Live stream player"
/>Browser Support
| Browser | HLS Support | | -------------- | --------------------------- | | Chrome | via hls.js | | Firefox | via hls.js | | Safari | Native (hls.js is bypassed) | | Edge | via hls.js | | Mobile Safari | Native (hls.js is bypassed) | | Android Chrome | via hls.js |
When a browser supports HLS natively (e.g., Safari), the component sets the src attribute directly on the <video> element and skips hls.js entirely.
Error Handling
The component handles hls.js errors automatically:
- Network errors — retries up to 3 times using
hls.startLoad(), with an increasing delay between each attempt (1s, 2s, 3s). - Media errors — calls
hls.recoverMediaError()to attempt recovery. - Fatal unrecoverable errors — destroys and reinitializes the player from scratch.
All errors are also surfaced via the onError callback if provided.
Styling
The rendered <video> element always has the class react-hls-player. You can target it in your stylesheet:
.react-hls-player {
width: 100%;
height: auto;
display: block;
}You can also pass additional classes via the className prop.
TypeScript
All types are exported from the package entry point.
import type {
ReactHlsPlayerProps,
ReactHlsPlayerRef,
HlsConfig,
PlayerPreset,
} from "@im03/react-hls-player";License
MIT — Ishant Mishra
