MediaStore React Hooks

If you would rather write your own react components, Media Chrome has a React <Context.Provider> and corresponding hooks to directly integrate with its MediaStore state management API. These are included in our standard npm package.

To use, simply import these from "media-chrome/react/media-store".

Media Chrome has a MediaStore “under the hood” that manages all media state and media state change requests.

If you’re familiar with React Redux and its useSelector() and useDispatch() hooks, working with Media Chrome’s MediaStore React integration should feel familiar. Here’s a basic example of using the provider and hooks:

import {
  MediaProvider,
  useMediaDispatch,
  useMediaSelector,
  useMediaRef,
  useMediaFullscreenRef,
  MediaActionTypes,
} from 'media-chrome/react/media-store';

const Video = () => {
  // "Wire up" the <video/> element to the MediaStore using useMediaRef()
  const mediaRef = useMediaRef();
  return (
    <video
      ref={mediaRef}
      style={{ width: '100vw' }}
      src="https://stream.mux.com/DS00Spx1CV902MCtPj5WknGlR102V5HFkDe/high.mp4"
      preload="auto"
      muted
      crossOrigin=""
    />
  );
};

const PlayerContainer = ({ children }) => {
  // "Wire up" the element you want the MediaStore to target for fullscreen using useMediaFullscreenRef()
  const mediaFullscreenRef = useMediaFullscreenRef();
  return (<div ref={mediaFullscreenRef}>{children }</div>);
};

const PlayButton = () => {
  // Dispatch media state change requests using useMediaDispatch()
  const dispatch = useMediaDispatch();
  // Get the latest media state you care about in your component using useMediaSelector()
  const mediaPaused = useMediaSelector(state => state.mediaPaused);
  return (
    <button
      onClick={() => {
        // Select from a set of well-defined actions for state change requests
        // using MediaActionTypes
        const type = mediaPaused
          ? MediaActionTypes.MEDIA_PLAY_REQUEST
          : MediaActionTypes.MEDIA_PAUSE_REQUEST;
        dispatch({ type });
      }}
    >
      {mediaPaused ? 'Play' : 'Pause'}
    </button>
  );
};

const FullscreenButton = () => {
  // Dispatch media state change requests using useMediaDispatch()
  const dispatch = useMediaDispatch();
  // Get the latest media state you care about in your component using useMediaSelector()
  const mediaIsFullscreen = useMediaSelector(state => state.mediaIsFullscreen);
  return (
    <button
      onClick={() => {
        // Select from a set of well-defined actions for state change requests
        // using MediaActionTypes
        const type = mediaIsFullscreen
          ? MediaActionTypes.MEDIA_EXIT_FULLSCREEN_REQUEST
          : MediaActionTypes.MEDIA_ENTER_FULLSCREEN_REQUEST;
        dispatch({ type });
      }}
    >
      {mediaIsFullscreen ? 'Exit fullscreen' : 'Enter fullscreen'}
    </button>
  );
};

const Player = () => {
  // Get access to Media Chrome's state management in your components using <MediaProvider/>
  // NOTE: Unlike many other providers (including react-redux's Provider), you'll likely want to keep
  // your <MediaProvider/> in or close to your <Player/> component)
  return (
    <MediaProvider>
      <PlayerContainer>
        <Video />
        <div>
          <PlayButton />
          <FullscreenButton />
        </div>
      </PlayerContainer>
    </MediaProvider>
  );
};

export default Player;

For a full simple example of working with MediaStore in React, check out our NextJS example page (source).

For a slightly more advanced example that uses Material UI and the <mux-video> custom media element, you can check out this other NextJS Example page (source).

This is the context provider for media state and media state change requests. Use it like you would other react context providers, i.e.:

return (
  <MediaProvider>
    {/* descendant components that
      - use media state,
      - make media state change requests, or
      - are the source of media state, e.g. a <video> component
    */}
  </MediaProvider>
);

This is the primary way to wire up a media element to both get the latest media state using useMediaSelector() and make state change requests using useMediaDispatch(). For most use cases, it’s as simple as:

const mediaRef = useMediaRef();
// ... additional code
return (
  <video
    ref={mediaRef}
    // ... additional props
  />
);

Note that the MediaStore also supports extended state like video renditions and audio tracks that are not available “out of the box” with e.g. a <video> element. To take advantage of these, you can rely on a web-component based custom media element like the <mux-video> custom media element, where you’d wire it up in exactly the same way, e.g.:

// import the custom element
import '@mux/mux-video';

// ...

const mediaRef = useMediaRef();
// ... additional code
return (
  <mux-video
    ref={mediaRef}
    // ... additional props
  />
);

You can find a slightly advanced example of this usage here.

In order to make sure your custom controls show up while the video is in fullscreen, you’ll need to wrap your UI up in a component that will serve as your root fullscreen element. To wire this up to the MediaStore, use useMediaFullscreenRef(). For most use cases, it’s as simple as:

const fullscreenRef = useMediaFullscreenRef();
// ... additional code
return (
  <div
    ref={fullscreenRef}
    // ... additional props
  >
  {/* UI controls, video element, etc. should be descendent components in here */}
  </div>
);

You can find a simple example of using useMediaFullscreenRef() here, or a slightly more advanced example here.

This is the primary way to get the latest media state from the MediaStore. To use it, you simply pass in a “selector function” to grab the state you need for a particular component, like this:

const mediaMuted = useMediaSelector(state => state.mediaMuted);

Below is a list of all available MediaState properties with descriptions.

NameTypeDescription
mediaPausedbooleantrue if the media is paused, otherwise false (i.e. when attempting to play).
mediaHasPlayedbooleantrue if the currently loaded media has played, otherwise false. Helpful for things like initial UI transitions or gating whether or not to show a loading indicator on initial media load.
mediaLoadingbooleantrue if the media is loading content needed to play for the current playhead time, otherwise false. Helpful for conditionally showing a loading indicator and possibly hiding/disabling other UI components.
mediaEndedbooleantrue if the media has ended, otherwise false. Gets reset from things like replaying the media.
mediaPlaybackRatenumberA number representing a multiplier for how fast media is playing or will play. The majority of playback environments have a minimum value of 0 and a maximum value of no more than 3.
mediaCurrentTimenumberA number representing the current playhead time of the media.
mediaDurationnumberA number representing the total duration of the currently loaded media. For live content that has not ended, this value should be Infinity.
mediaSeekable[number,number]A tuple representing the earliest and latest media time, respectively, that can be played or seeked to for the media. For non-live media, this will typically be [0, mediaDuration].
mediaBuffered[number,number][]An array of tuples representing the media currently loaded and buffered for playback. Helpful for “fancier” seek bar UIs that indicate what’s in the buffer.
mediaMutedbooleantrue if the media is muted, otherwise false.
mediaVolumenumberA number from 0 to 1 representing the current volume of the media. Not available in all playback environments.
mediaVolumeLevelVolumeLevelsA convenience state that combines the muted and volume states and translates it into a set of well-defined “levels”. Helpful for things like dynamic icons on mute/volume buttons indicating the current effective volume.
mediaIsFullscreenbooleantrue when playback is in fullscreen mode, otherwise false. Not available in all playback environments.
mediaIsPipbooleantrue when playback is in picture-in-picture mode, otherwise false. Not available in all playback environments.
mediaSubtitlesListTextTrackLike[]A list of all subtitle and closed caption tracks available for the currently loaded media. If empty, that means no subtitles/captions are currently available for the media.
mediaSubtitlesShowingTextTrackLike[]A list of all subtitle and closed caption tracks currently showing. Typically will be at most one.
mediaChaptersCuesCueLike[]A list of all available chapter objects for the currently loaded media, based on the first “chapters” TextTrack available.
mediaPreviewTimenumberA number representing the the media time to “preview”. Helpful for things like “fancier” seek bar UIs that display preview information when e.g. hovering over a particular position. Used to derive other preview state properties. Setting to undefined will clear all preview state properties. See the mediapreviewrequest state change request type for more.
mediaPreviewImagestringThe URL for a preview image tiles source, derived from a TextTrack that conforms with this implementation, determined by the currently set mediaPreviewTime.
mediaPreviewCoords[number, number, number, number]The [x, y, width, height] coordinates within the mediaPreviewImage’s tiles for the specific tile image to preview, determined by the currently set mediaPreviewTime.
mediaPreviewChapterstringThe text from the mediaPreviewChaptersCues cue to preview, determined by the currently set mediaPreviewTime
mediaStreamTypeStreamTypes[]A value representing whether the currently loaded media is "live" or "on-demand", with "unknown" meaning that not enough information has been loaded for the media to determine the stream typeof. Helpful for displaying different UIs for "live" vs. "on-demand".

* Best with a custom media element like <mux-video>, with a fallback of inferring "live" when duration is Infinity.
mediaTargetLiveWindownumberA number representing how far back from the live edge (mediaSeekable end) a user should be allowed to seek, in seconds, for “DVR”-style functionality, where Infinity means users can seek back to mediaSeekable start. Only applicable when mediaStreamType is "live". Helpful for customizing different UIs for “DVR” vs. “non-DVR” "live".

* Requires a custom media element like <mux-video>.
mediaTimeIsLivebooleanA boolean representing when playback at a particular playhead be treated as “playing live”. Only applicable when mediaStreamType is "live". Helpful for live indicator UIs.

* Best with a custom media element like <mux-video>, with a fallback of playing 10 within ten seconds of the live edge (mediaSeekable end) as playing live.
mediaRenditionListRendition[]A list of the available video renditions, or quality levels, for the currently loaded media. Helpful for rendition/quality selector UIs.

* Requires a custom media element like <mux-video>.
mediaRenditionSelectedstring|undefinedid of the currently selected video rendition, where undefined means renditions will be automatically selected/changed, typically based on things like network conditions. Helpful for rendition/quality selector UIs.

* Requires a custom media element like <mux-video>.
mediaAudioTrackList[AudioTrack[]]A list of the available audio tracks for the currently loaded media. Helpful for audio selector UIs.

* Requires a custom media element like <mux-video>.
mediaAudioTrackEnabledstringid of the currently enabled (aka audible when playing with volume) audio track

* Requires a custom media element like <mux-video>.
mediaIsCastingbooleantrue when playback is currently being played on a remote device using Google Cast. Not available in all playback environments.

* Requires a custom media element like <mux-video>.
mediaIsAirplayingbooleantrue when playback is currently being played on a remote device using Apple AirPlay. Not available in all playback environments.
mediaVolumeUnavailableAvailabilityStates|undefinedDescribes the availability of volume control in your current playback environment. undefined means feature is available.
mediaFullscreenUnavailableAvailabilityStates|undefinedDescribes the availability of watching in fullscreen mode in your current playback environment. undefined means feature is available.
mediaPipUnavailableAvailabilityStates|undefinedDescribes the availability of watching in picture-in-picture mode in your current playback environment. undefined means feature is available.
mediaRenditionUnavailableAvailabilityStates|undefinedDescribes the availability of renditions (i.e. different quality/resolution versions of the media) in your current playback environment. undefined means feature is available.
mediaAudioTrackUnavailableAvailabilityStates|undefinedDescribes the availability of multiple/alternate audio tracks in your current playback environment. undefined means feature is available.
mediaCastUnavailableAvailabilityStates|undefinedDescribes the availability of Google Cast in your current playback environment. undefined means feature is available.
mediaAirplayUnavailableAvailabilityStates|undefinedDescribes the availability of AirPlay in your current playback environment. undefined means feature is available.

Availability states describe whether or not a particular feature or state is usable in the current context. By convention, the availability media state property’s name corresponds to the other, related media state(s) and state change request types it describes. So e.g. mediaVolumeUnavailable corresponds to the mediaVolume state and the "mediavolumerequest" state change request type; "mediaCastUnavailable" corresponds to the mediaIsCasting state and the "mediaentercastrequest" and "mediaexitcastrequest" state change request types; and so on.

Also by convention, these state properties will all describe different kinds of unavailability. This allows users to ignore availability states for simple UI implementations. It also allows users to treat availability states as “truthy” values when they don’t need to distinguish between different types of (un)availability. For example:

const airplayAvailable = useSelector(state => !state.mediaAirplayUnavailable);

Below are the different categories of unavailability. As mentioned above, undefined means the feature is available in the current playback context. For convenience, we provide the AvailabilityStates constants object as one of our exports in the media-chrome/react/media-store module, with the following key/value pairs:

KeyValueDescription
UNAVAILABLE"unavailable"This means the corresponding state and functionality is currently unavailable in this playback context, but may become available at a later point. Examples include AirPlay in Safari while no remote playback targets are detected on the network or using a custom media element that supports multiple audio tracks but playing media that only has one (or no) audio tracks.
UNSUPPORTED"unsupported"This means the corresponding state and functionality will never be available in this playback context. Examples include picture-in-picture on FireFox or AirPlay on non-Safari browsers.

Volume levels, provided via the mediaVolumeLevel state, are a convenience state based on the mediaVolume and mediaMuted states which indicate how loud the “effective volume” currently. For convenience, we provide the VolumeLevels constants object as one of our exports in the media-chrome/react/media-store module, with the following key/value pairs:

KeyValueDescription
HIGH"high"Means the media is unmuted and the volume is >= 75% (0.75).
MEDIUM"medium"Means the media is unmuted and the volume is >= 50% and <75% (0.5, 0.75 respectively).
LOW"low"Means the media is unmuted and the volume is < 50% but not 0% (0.5, 0 respectively).
OFF"off"Means the media is either muted or set to 0% (0).

Stream types describe whether the currently loaded media is a live stream or on demand. This can be helpful if your UI will potentially be used for both live and on demand content, and you want to be able to show different controls for each (e.g. hiding your seek bar for live). For full functionality, this relies on custom media elements like <mux-video>, though the MediaStore will fall back to checking if the media element has a duration of Infinity just in case you’re just using e.g. a <video> element with hls.js. Since this is determined based on the loaded media, which is an asynchronous process, there is also an "unknown" value for initial load cases. For convenience, we provide the StreamTypes constants object as one of our exports in the media-chrome/react/media-store module, with the following key/value pairs:

KeyValueDescription
LIVE"live"Means the currently loaded media is a live stream.
ON_DEMAND"on-demand"Means the currently loaded media is on demand or “static” content (e.g. a simple mp4 source).
UNKNOWN"unknown"Means the media hasn’t loaded enough information yet to determine whether it’s live or on demand.

For more on stream types, check out the docs.

Subtitles objects are the sorts of values provided by captions/subtitles state modeled by the MediaStore (e.g. mediaSubtitlesList). They’re intentionally “shaped” like a TextTrack object, specifically the kind, label, and language properties.

Cue objects are the sorts of values provided by state modeled by the MediaStore provided by a text track’s cues (e.g. mediaChaptersCues). They’re intentionally “shaped” like a VTTCue object, specifically the text, startTime, and endTime properties.

Rendition objects are the sorts of values provided to represent different video renditions, or “qualities” for a media stream for MediaStore state like mediaRenditionList. This information isn’t natively supported by an HTMLMediaElement, so it will require a custom media element like <mux-video>. The object looks like this:

type Rendition = {
  src?: string;
  id?: string;
  width?: number;
  height?: number;
  bitrate?: number;
  frameRate?: number;
  codec?: string;
  readonly selected: boolean;
};

For more information and a “mixin” implementation, see the media-tracks project.

Audio Track objects are the sorts of values provided to represent different audio tracks such as alternative languages or descriptive audio for visually impaired folks. This information isn’t natively supported by an HTMLMediaElement, so it will require a custom media element like <mux-video>. The object looks like this:

type AudioTrack = {
  id?: string;
  kind?: string;
  label: string;
  language: string;
  enabled: boolean;
};

For more information and a “mixin” implementation, see the media-tracks project.

This is the primary way to make media state change requests to the MediaStore. To use it, you simply invoke the dispatch function returned from useMediaDispatch() with the action type (and detail, where relevant), like this:

const dispatch = useMediaDispatch();
// ... additional code
return (
  <button onClick={() => {
    const type = MediaActionTypes.MEDIA_TOGGLE_SUBTITLES_REQUEST;
    dispatch({ type });
  }}>Toggle Subtitles</button>
);

Below is a list of all available MediaActionTypes for media state change requests with descriptions.

For convience, we provide a MediaActionTypes constants object for all well defined media state change request types. Our naming convention is to use “CONSTANT_CASE” (aka UPPERCASE_SNAKE_CASE) for the keys, so e.g. 'mediaplayrequest' would be MediaActionTypes.MEDIA_PLAY_REQUEST.

keytypedetailDescription
MEDIA_PLAY_REQUEST'mediaplayrequest'N/ARequest beginning/renewing playback.
MEDIA_PAUSE_REQUEST'mediapauserequest'N/ARequest pausing playback.
MEDIA_MUTE_REQUEST'mediamuterequest'N/ARequest muting volume.
MEDIA_UNMUTE_REQUEST'mediaunmuterequest'N/ARequest unmuting volume.
MEDIA_VOLUME_REQUEST'mediavolumerequest'number (0,1)Request changing the volume.
MEDIA_SEEK_REQUEST'mediaseekrequest'numberRequest seeking to some time on the media timeline.
MEDIA_SEEK_TO_LIVE_REQUEST'mediaseektoliverequest'N/ARequest seeking to the live edge of the media. Only relevant for live/DVR.
MEDIA_PLAYBACK_RATE_REQUEST'mediaplaybackraterequest'number (0,3)Request changing the rate of media playback.
MEDIA_ENTER_FULLSCREEN_REQUEST'mediaenterfullscreenrequest'N/ARequest entering fullscreen mode.
MEDIA_EXIT_FULLSCREEN_REQUEST'mediaexitfullscreenrequest'N/ARequest exiting fullscreen mode.
MEDIA_ENTER_PIP_REQUEST'mediaenterpiprequest'N/ARequest entering picture-in-picture mode.
MEDIA_EXIT_PIP_REQUEST'mediaexitpiprequest'N/ARequest exiting picture-in-picture mode.
MEDIA_SHOW_SUBTITLES_REQUEST'mediashowsubtitlesrequest'TextTrackLike|TextTrackLike[]Request to show one or more of the available subtitles during playback.
MEDIA_DISABLE_SUBTITLES_REQUEST'mediadisablesubtitlesrequest'TextTrackLike|TextTrackLike[]Request to disable one or more of the currently showing subtitles.
MEDIA_TOGGLE_SUBTITLES_REQUEST'mediatogglesubtitlesrequest'boolean?Request to toggle between showing or disabling subtitles, with an optional detail to expicitly request showing (true) or disabled (false). When more than one subtitle/caption track is available, MediaStore will rely on heuristics like prior user selections and navigator.languages to pick which subtitle to show.
MEDIA_PREVIEW_REQUEST'mediapreviewrequest'numberRequest available preview state for some time on the media timeline.
MEDIA_AIRPLAY_REQUEST'mediaairplayrequest'N/ARequest Apple AirPlay remote playback.
MEDIA_ENTER_CAST_REQUEST'mediaentercastrequest'N/ARequest starting Google Cast remote playback.
MEDIA_EXIT_CAST_REQUEST'mediaexitcastrequest'N/ARequest ending Google Cast remote playback.
MEDIA_RENDITION_REQUEST'mediarenditionrequest'string (id)Request playing a specific video rendition. A value of undefined means the media element should automatically select renditions (typically based on e.g. network conditions and the like).
MEDIA_AUDIO_TRACK_REQUEST'mediaaudiotrackrequest'string (id)Request playing a specific audio track (e.g. audio in a different language).