Quality
A menu of quality levels, from your own files or from an HLS or DASH engine.
import { quality } from "react-video-kit/quality";Adds 1.4 KB to a player3.1 KB on its ownSource
Adds a Quality row to the settings menu. Play the demo, open the settings menu, and pick another level. The time carries on from the same place, and the picture size under the player changes.
Picture size: not loaded yet
"use client";import { useState } from "react";import { VideoPlayer } from "react-video-kit/player";import { quality } from "react-video-kit/quality";const plugins = [quality()];const source = { renditions: [ { src: "/media/sample-720.mp4", type: "video/mp4", label: "720p", height: 720 }, { src: "/media/sample-360.mp4", type: "video/mp4", label: "360p", height: 360 }, { src: "/media/sample-270.mp4", type: "video/mp4", label: "270p", height: 270 }, ],};export default function Quality() { const [size, setSize] = useState("not loaded yet"); return ( <> <VideoPlayer src={source} poster="/media/poster.jpg" plugins={plugins} load="visible" media={{ onLoadedMetadata: ({ currentTarget: video }) => setSize(`${video.videoWidth}×${video.videoHeight}`), }} /> <p className="mt-3 text-sm text-current opacity-80">Picture size: {size}</p> </> );}Usage
quality() takes no options. What it offers depends on the source. For progressive files, pass { renditions: [...] } as src:
import { VideoPlayer } from "react-video-kit/player";import { quality } from "react-video-kit/quality";const plugins = [quality()];const source = { renditions: [ { src: "/video-1080.mp4", type: "video/mp4", label: "1080p", height: 1080 }, { src: "/video-720.mp4", type: "video/mp4", label: "720p", height: 720 }, ], initial: 1,};<VideoPlayer src={source} plugins={plugins} />;labelis the name in the menu. Without one, the menu shows theheightas720p, and without either, the rendition's position in the list.heightsets the order. The menu lists the tallest first, and renditions with no height last.initialis the index of the rendition to start on. It defaults to the first.
For HLS and DASH, add the engine's plugin and pass the manifest. defaultPlugins() already includes quality():
import { hls } from "react-video-kit/hls";import { defaultPlugins, VideoPlayer } from "react-video-kit/player";const plugins = [hls(), ...defaultPlugins()];<VideoPlayer src="/video.m3u8" plugins={plugins} />;Progressive files
Each rendition is a separate file, so the viewer does the choosing. The menu lists every rendition, with no Auto, and the row shows the one that's playing.
Picking one loads the new file into the same video element. The player restores the position and rate once the file's metadata arrives, then resumes if it was playing as soon as the file can play. The row changes at that point.
If the new file fails, the player goes back to the previous rendition at the same position, and sets a non-fatal error with the code "quality-switch" on media.error. The error overlay stays hidden, because it only shows fatal errors, and onError hears about it.
A plain array of sources isn't a list of renditions. It's a list of fallbacks, tried in order, and it gives no menu.
HLS and DASH
The engine reports the levels in the manifest. The menu lists Auto, then each level, tallest first and named by height, such as 720p.
"use client";import { hls } from "react-video-kit/hls";import { defaultPlugins, VideoPlayer } from "react-video-kit/player";export default function HlsEngine() { return ( <VideoPlayer src="/media/hls/master.m3u8" poster="/media/poster.jpg" plugins={[hls({ preferEngine: true }), ...defaultPlugins()]} load="visible" /> );}- Auto: the engine picks the level. The row reads
Auto 720p, naming the level it's on. - Manual: picking a level turns auto off, and the row shows that level alone. Pick Auto to hand control back.
The engines switch differently. hls.js keeps what's already buffered, so playback doesn't stall. dash.js replaces what's buffered, so the viewer's choice shows right away.
Safari's native HLS
On Apple browsers, hls() leaves HLS to the browser unless you ask otherwise. The browser reports no levels, so the Quality row doesn't appear and the browser picks the level itself. To offer a choice there, use hls.js:
import { hls } from "react-video-kit/hls";hls({ preferEngine: true });This needs Media Source support. Where there isn't any, the browser's native HLS plays instead.
Build your own control
Levels are part of the player's media state, so read them with useVideoState, and change them with the plugin's select.
"use client";import { useCommands, useVideoState } from "react-video-kit";import { quality } from "react-video-kit/quality";function QualityButtons() { const levels = useVideoState((media) => media.qualities); const active = useVideoState((media) => media.quality.active); const canAuto = useVideoState((media) => media.quality.canAuto); const { select } = useCommands(quality); return ( <> {canAuto ? <button onClick={() => select("auto")}>Auto</button> : null} {levels.map((level) => ( <button key={level.id} aria-pressed={level.id === active} onClick={() => select(level.id)}> {level.label ?? level.id} </button> ))} </> );}Place the menu yourself
QualityMenu is the row that appears in the settings menu. When you compose your own layout, put it in any menu:
import * as Video from "react-video-kit";import { QualityMenu } from "react-video-kit/quality";<Video.Menu> <Video.MenuTrigger aria-label="Quality">HD</Video.MenuTrigger> <Video.MenuContent> <QualityMenu /> </Video.MenuContent></Video.Menu>;Behaviour
QualityMenurenders nothing with fewer than two levels, such as for a plain MP4 or a stream with one level.- A
renditionssource still plays without this plugin, on itsinitialrendition, with no way to switch. - Give each rendition its own
labelorheight. They double as its ID, so two with the same name can't be told apart. select("auto")does nothing for renditions, and neither does picking the rendition that's playing.- Levels reset with each new source.
- The analytics plugin reports each change as a
quality-changeevent. The persistence plugin doesn't remember the level.
API
Options
This plugin takes no options.
State
This plugin has no state of its own.
Commands
Call them with useCommands(quality).
| Command | Type | Description |
|---|---|---|
| select | (id: string | "auto") => void | Switches to this level, or to automatic choice with auto if the provider offers it. |
Parts
| Part | Description |
|---|---|
| QualityMenu | Renders nothing unless there's more than one level to choose from. |