Published on November 16, 2021 (over 2 years ago)

Mux-flavored HTML video tag

Dylan Jhaveri
By Dylan Jhaveri6 min readProduct

Most web developers are familiar with the HTML 5 video tag:

<video controls src="my-video.mp4"></video>

There’s just one problem with this: without extra help, HLS adaptive bitrate streaming will only work in Safari. If you try this <video controls src="{playback_ID}.m3u8 />, it won’t work in most other browsers.

So that’s why we’re introducing the mux-video HTML video tag:

<mux-video controls playback-id="your-playback-id" env-key="your-mux-data-env-key" ></mux-video>

You can now use the <mux-video> element the same way you would use the <video> element. This is the easiest and smoothest way to make sure you have reliable HLS playback and Mux Data correctly instrumented.

LinkHow does this work and what does this do for me?

If you’re using the built-in HTML <video> tag with Hls.js, use this as a drop-in replacement. Previously, you might have had code like this:

<video id="my-player" controls ></video> <script> const video = document.querySelector('#my-player'); const src = '{PLAYBACK_ID}.m3u8'; if (video.canPlayType('application/')) { // Some browsers (safari and ie edge) support HLS natively video.src = src; } else if (Hls.isSupported()) { const hls = new Hls(); hls.loadSource(src) hls.attachMedia(video); } else { console.error("This is a legacy browser that doesn't support MSE"); } </script>

You can scrap all that and drop this instead:

<mux-video playback-id={PLAYBACK_ID}></mux-video>

The <mux-video> element will automatically set up and configure Hls.js with an optimal config and handle edge cases under the hood like retrying on recoverable errors.

To make sure you get the optimal configuration settings, pass in the stream-type:

<mux-video controls playback-id={PLAYBACK_ID} stream-type="on-demand"> <!-- use "on-demand" for video on demand --> </mux-video> <mux-video controls playback-id={PLAYBACK_ID} stream-type="live"> <!-- use "live" for HLS live streaming --> </mux-video> <mux-video controls playback-id={PLAYBACK_ID} stream-type="ll-live"> <!-- use "ll-live" for low latency live streaming --> </mux-video>

Note that if you’re using Signed URLs for playback security, pass in the token like this as a query parameter:


Query params work the same way for any playback modifier:


LinkAnd I get Data, too?

Yup, pass in your Mux Data environment key via the `env-key` attribute and pass in metadata fields.

At minimum, to make sure the data you’re getting in Mux Data is useful, you should pass in attributes for:

  • metadata-video-title: This is an arbitrary title for your video. A good value to use for this would be the title you use for the video in your application.
  • metadata-viewer-user-id: This is the internal ID of the user watching this video in your application. Note that this value should not contain any personally identifiable information like an email address or a username; it should be an anonymous number or string that maps back to a user in your database.
  • metadata-video-id: This is an arbitrary ID for your video. Similar to user ID, this value should map back to an ID in your database.
<mux-video controls playback-id={PLAYBACK_ID} stream-type="vod" env-key="{ENV_KEY}" metadata-video-title="Big Buck Bunny" metadata-viewer-user-id="user-id-1234" metadata-video-id="video-id-1234" />

That’s a great start to making your Mux Data useful. With these fields, you can do things like see how many people are watching, break out the Overall Viewer Experience score by video title, or you can filter all video views by a particular user ID, which is great when tracking down a specific support issue. There are more metadata fields to leverage, and you can provide the rest through the metadata setter on the mux-video element like this:

<mux-video playback-id="{PLAYBACK_ID}" env-key="mux-data-env-key" metadata-video-title="Big Buck Bunny" metadata-viewer-user-id="user-id-1234" controls > </mux-video> <script> const muxVideo = document.querySelector("mux-video"); muxVideo.metadata = { experiment_name: "landing_page_v3", video_content_type: "clip", video_series: "season 1", }; </script>

Previously, to get Mux Data you would have had to find the right Data SDK and integrate it yourself--<mux-video> handles this for you and makes integrating Data seamless.

If you haven't integrated Mux Data yet, now is a great time since we recently made it easier to use Data + Video together and have expanded the free tier.

LinkVideo is cool, but have you heard about audio?

Yup <mux-audio> is a thing too that replaces the <audio> element, it works the same way.

LinkCan I use this in React|Vue|Svelte|Angular|etc. ?

You bet you can! These are plain old HTML elements that work with native browser APIs. After you import or require the library into your code then you can use the <mux-video> tag the same way you would use a <video> tag in these frameworks.

Down the road, we plan to release some library-specific implementations for these elements, but for now, use these HTML tags just like you would any other HTML element in your application.

Here’s a React example:

function VideoPlayer ({ playbackId, envKey, title }) { return ( <mux-video controls playback-id={playbackId} env-key={envKey} metadata-video-title={title} /> ) }

LinkHow do I style this thing?

You’ll notice that the default HTML <video> element uses built-in browser styles. Since <mux-video> is meant to be a drop-in replacement, it works the same way. When it comes to styling, you can adjust sizing, spacing etc with CSS:

mux-video { width: 100%; max-width: 600px; margin: 40px auto; }

But if you’re using the controls attribute, you get what the browser gives you. So how can you add custom controls?

First off, drop the controls attribute so you have a bare video player.

You can certainly build your own custom controls with buttons, sliders, and other HTML elements.  To do that, you would have to set up event listeners for user interactions on your own controls and then call play(), pause(), etc. on the mux-video element itself:

You can also check out media-chrome! Media-chrome is a new library we’ve been quietly working on behind the scenes. It’s not really a player per say--it’s a collection of UI components for building players. It works with the HTML video element, or any element that behaves like the HTML video element. All you have to do is add the slot=”media” attribute to the video element, and then you can wire up controls and style them yourself.

<media-controller> <mux-video playback-id={PLAYBACK_ID} slot="media" /> <media-control-bar> <media-play-button></media-play-button> <media-time-range></media-time-range> <media-time-display show-duration></media-time-display> </media-control-bar> </media-controller>

There’s more to unpack there, but that’s how you can get started. Here’s a larger example on codesandbox using <mux-video> with media-chrome.If you are building with media-chrome, please book a time with me so we can chat about you’re using it and help you get set up and answer any questions you have.

LinkUse <mux-video> today

Install with npm or yarn:


npm i @mux/mux-video

Or yarn:

yarn add @mux/mux-video

Import into your javascript bundle

import '@mux/mux-video'

Or use require depending on how your environment is set up:


Alternatively, load the element directly from the cdn:

<script src="" ></script>

Here it is on Github.

LinkMux Elements

The mux-video and mux-audio elements kick off a larger muxinc/elements monorepo. We’re aiming to build more elements to make your life easier as an application developer working with Mux. Beyond just video playback there’s a whole host of UI concerns that developers working with Mux commonly have to build into their applications. What else would you like to see? An uploader component, perhaps? Widgets for showing concurrent viewer counts? We’d love to hear your ideas.

Written By

Dylan Jhaveri

Software Engineer and cold water surfer. Previously startup co-founder. Trying to find the best cheeseburger in San Francisco.

Leave your wallet where it is

No credit card required to get started.