Media

The video or audio element the player controls, and a poster image shown before or after playback.

import { Media } from 'react-media-kit'
import Hls from 'hls.js'
import { Player, Media, type MediaEngine } from 'react-media-kit'
import './stream-player.css'

const SRC = '/stream/master.m3u8'

const attachHls: MediaEngine<HTMLVideoElement> = (video) => {
  // Safari plays HLS on its own
  if (video.canPlayType('application/vnd.apple.mpegurl')) {
    video.src = SRC
    return
  }
  const hls = new Hls()
  hls.loadSource(SRC)
  hls.attachMedia(video)
  return () => hls.destroy()
}

export function StreamPlayer() {
  return (
    <Player.Root>
      <Player.Container className="player">
        <Media.Video attachEngine={attachHls} playsInline />
        <Media.Poster src="/poster.webp" alt="" visibleWhen="both" className="poster" />
        <Player.Overlay label="Play or pause" className="player-overlay" />
      </Player.Container>
    </Player.Root>
  )
}

Pass a plain src for files the browser can play on its own. For adaptive streams, attachEngine hands you the element so a library like hls.js can attach itself.

Anatomy

How the parts nest.

<Player.Container>
  <Media.Video />
  <Media.Poster />
</Player.Container>

API reference

Parts accept the attributes of the element they render, plus a ref, except where noted. Every part that renders an element, except Media.Poster, also sets the media state attributes.

Media.Video

renders <video>

The <video> element the player controls. Render it inside Player.Container so it goes fullscreen with the player. Pass src or attachEngine, or neither if you use <source> children.

Only one Media.Video or Media.Audio is allowed per Player.Root. A second one throws a ReactMediaKitError.

PropTypeDefaultDescription
srcstring—Media URL. Passing it together with attachEngine throws a ReactMediaKitError.
attachEngineMediaEngine<HTMLVideoElement>—Called once when the <video> element mounts, for example to attach hls.js, and may return a cleanup function. Changing the function later doesn’t call it again.

Media.Audio

renders <audio>

The <audio> element the player controls, for audio-only players. Pass src or attachEngine, or neither if you use <source> children. As with Media.Video, only one media element is allowed per Player.Root.

PropTypeDefaultDescription
srcstring—Media URL. Passing it together with attachEngine throws a ReactMediaKitError.
attachEngineMediaEngine<HTMLAudioElement>—Called once when the <audio> element mounts, and may return a cleanup function. Changing the function later doesn’t call it again.

Media.Poster

renders <img>

An image over the media that shows before playback starts, after it ends, or both. It renders an <img>, or a <picture> with <source>s when you pass sources.

It is positioned over the media (position: absolute; inset: 0) and ignores pointer events, but it doesn’t size or hide itself. Give it a size, and hide it with CSS when data-visible isn’t set, as in the example. It also gets aria-hidden while it should be hidden.

PropTypeDefaultDescription
srcstringrequiredImage URL.
altstringrequiredAlternative text. Pass "" if the poster is decorative.
sourcesSourceHTMLAttributes[]—Responsive <source> elements. When non-empty, a <picture> is rendered.
visibleWhen"pending" | "ended" | "both""pending""pending" shows the poster before playback starts, "ended" after it ends, and "both" at both times.
Data attributeDescription
data-visibleThe poster should show.

ref isn’t accepted. With sources, className and style go on the <picture>, together with the aria-hidden and data-visible the poster sets, and every other attribute goes on the <img>.

Media state attributes

Five of these describe loading, and exactly one of them is set at a time. The other three mark features the browser lacks. Use them to hide controls that can’t work, or to style the player while it loads.

Data attributeDescription
data-media-pendingLoading hasn’t started. This is the initial state.
data-media-loadingThe media has started loading.
data-media-metadata-loadedThe duration and dimensions are known, but there isn’t enough data to play yet.
data-media-playableThe media can play. It stays set if playback later stalls to buffer.
data-media-errorThe media element reported an error, such as a network or decoding failure. A blocked play() call doesn’t set it; use onError for that.
data-fullscreen-unsupportedThe browser can’t go fullscreen, or support hasn’t been detected yet.
data-pip-unsupportedPicture-in-picture isn’t available, such as for audio, or support hasn’t been detected yet.
data-volume-change-unsupportedThe browser doesn’t let scripts set the volume, such as on iOS, or support hasn’t been detected yet.