Media Chrome builds player controls out of HTML tags you already know
Custom elements (web components) for making audio and video player controls that look great in your website or app.
At a glance
- What is it?
- Web components from the creators of Video.js at Mux, giving you customisable audio and video controls that work with any framework and with plain video, audio, HLS.js or YouTube embeds.
- Who is it for?
- Media Chrome is a good fit when you need a player that matches your design system and works with whatever media element you already have, particularly when that element changes between sources. It is the wrong choice when you need subtitles or DRM beyond what the underlying element provides, or when a managed player would save more time than the styling work costs.
- Can I use it commercially?
- Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 3 days ago.
- What is it written in?
- Mainly TypeScript, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 11, 2026, and from our analysis. They are not legal advice.
Editorial analysis
Player chrome as custom elements, not as a player
The distinction at the heart of Media Chrome is that it is not a player. It does not decode video, it does not implement streaming, and it does not own your media. What it provides is the control surface: the buttons, the scrub bar, the volume control, the time display, the settings menu.
Those are custom elements, which is the reason the whole approach works. `<media-controller>` wraps the media, `<media-control-bar>` groups the controls, and individual elements like `<media-play-button>` or `<media-time-range>` sit inside it. The video or audio element goes in the controller with `slot="media"`, and everything else is markup a designer already understands. Adding or removing a control means editing HTML, not wiring JavaScript.
The README's four checkmarks are the pitch in full: compatible with any JavaScript framework including React, Angular and Svelte; compatible with the `<video>` and `<audio>` elements and with players such as YouTube and HLS.js; simple HTML to add and remove controls; and simple CSS to style them. The fourth point is the one that decides most adoptions. Because the controls are styled with CSS custom properties rather than a JS theme API, they inherit your palette rather than asking you to configure a plugin.
The project comes from Mux and from the creator of Video.js, which tells you where the design experience came from.
A first video player from a single script tag
The README's video example needs one script tag and a controller element. Here it is, trimmed to the structural parts:
<media-controller>
<video
slot="media"
src="https://stream.mux.com/DS00Spx1CV902MCtPj5WknGlR102V5HFkDe/high.mp4"
crossorigin
>
</video>
<media-control-bar>
<media-play-button></media-play-button>
<media-mute-button></media-mute-button>
<media-volume-range></media-volume-range>
<media-time-range></media-time-range>
</media-control-bar>
</media-controller>The `crossorigin` attribute is not decoration. The full example pairs it with a `<track>` element pointing at a WebVTT storyboard file served from image.mux.com, labelled thumbnails and marked as metadata, which is how Mux shows scrub previews inside the time range. Serving media and metadata cross origin is what makes that work.
The control bar in the README goes further than the snippet above, adding a picture-in-picture button and a fullscreen button. Both are single tags, and that is the point: the feature set of your player is decided by which elements you put in the bar.
If you would rather not copy markup, the repository ships three runnable examples: `examples/vanilla/`, `examples/nextjs-with-typescript/` and `examples/vite-react-with-typescript/`. A deployed set of all repo examples is linked from the README as well.
Audio uses the same controller with an audio attribute
The audio example reuses every concept from the video one. The differences are an `audio` attribute on the controller, an `<audio>` element with the same `slot="media"`, and a control bar tuned for listening rather than watching:
<media-controller audio>
<audio
slot="media"
src="https://stream.mux.com/O4h5z00885HEucNNa1rV02wZapcGp01FXXoJd35AHmGX7g/audio.m4a"
></audio>
<media-control-bar>
<media-play-button></media-play-button>
<media-time-display showduration></media-time-display>
<media-time-range></media-time-range>
<media-playback-rate-button></media-playback-rate-button>
</media-control-bar>
</media-controller>Two details here are worth copying. `<media-time-display showduration>` renders both the current time and the total, which is the single most useful tweak for an audio player, and `<media-playback-rate-button>` gives speed control without a settings menu. The full README example also includes mute and volume range.
That an audio player is the same component set with one attribute rather than a separate library is the clearest evidence that the architecture is not video specific. The repository also hosts documentation for compatible media elements, which is the page to read if you are attaching the controller to something less obvious than a plain video tag.
Why Media Chrome exists instead of the native controls
The README's argument is about a specific recurring frustration: designers build a careful theme and then ship the browser's default controls anyway.
Three reasons it happens are given. Browser built-in controls cannot be restyled properly and look different in every browser. Social platforms like YouTube, Vimeo and SoundCloud only let you change small details, such as the primary button colour. And media controls are genuinely complex to build from scratch, which is why open source players like Video.js and JW Player exist, though those require you to learn proprietary JavaScript APIs and can be awkward with popular frameworks.
That last point is the real competitive set. Video.js and JW Player are full players with their own media handling and their own theming model. Media Chrome takes the opposite position: keep your media element, take only the controls, and let CSS do the styling. If you already have a working HLS or DASH stack, swapping the control layer is a smaller change than swapping the player. If you do not have a media stack, this is not where you should start.
The README also notes the component set can be used across players in the same site, for instance YouTube alongside a `<video>` element, and even inside a player as its own built-in controls.
React wrappers exist because web components feel wrong in React
Media Chrome is framework agnostic, but the README is honest that using custom elements directly in React can be clunky and that some things do not feel idiomatic. The example given is having to write `class=` instead of `className=`, which React treats specially for custom elements.
The response is a set of React wrapper components published for the core elements, documented on the media-chrome.org React get started page. This is the common pattern for web component libraries in React, and it is worth noting what it means in practice: you are not losing the web component implementation, you are adding a thin typed layer over it, so you keep CSS styling and lose none of the platform behaviour.
The packaging reflects that structure. `package.json` at version 4.19.2 exposes separate entry points: the root import, `./menu`, `./react`, `./react/menu`, and `./dist/react`, each with ES module, CommonJS and type declaration paths. The package is ESM first, with its module type declared in `package.json` and `main` pointing at `dist/index.js`, plus a CommonJS build under `dist/cjs/` for older toolchains. `typesVersions` maps the React wrapper types, which is how editors find them.
The project is MIT licensed and TypeScript throughout, with `tsconfig.json`, a `test/` directory and `CONTRIBUTING.md` in the tree.
Recent releases are small accessibility and browser fixes
Three releases are published and none is a feature release, which is the clearest signal about where this project is in its life. Media Chrome is past the point of adding controls and is now being hardened against browser differences.
v4.19.0 on 2026-04-13 is the largest of the three. It replaced `aria-valuetext` with `aria-description` for accessibility, added German localisation as part of the i18n work, added an auto rendition label for vertical videos, replaced a low resolution 48x48 favicon with a proper 1200x630 Open Graph image, and moved the focus-visible box shadow to a CSS variable.
v4.19.1 on 2026-06-01 is three fixes: inline-flex alignment on the icon slot so SVGs centre properly inside buttons, a margin fix on a range input on focus, and rounding the playback rate display to avoid float drift in Safari.
v4.19.2 on 2026-06-10 is a single change, treating only a remote state of `connected` as casting.
Read together, those are the kinds of bugs you only find by shipping to real browsers: Safari float drift in a display string, icon alignment in one engine, casting state in one browser. A component library at version 4 with this release pattern is one that people are using in production. The repository is not archived and the last push was on 2026-08-21.
Editorial conclusion
Media Chrome is a good fit when you need a player that matches your design system and works with whatever media element you already have, particularly when that element changes between sources. It is the wrong choice when you need subtitles or DRM beyond what the underlying element provides, or when a managed player would save more time than the styling work costs. The single most useful thing to check first is whether your media stack exposes what the controls need: Media Chrome attaches to a slotted media element and drives its properties, so an element that will not report duration, or that does not support a track, will limit what the control bar can honestly show. Start from the video example in the README, replace the demo src with your own asset, and check the architecture and design principles pages on media-chrome.org before committing to a component set.
Frequently asked questions
Is Media Chrome a video player?
No, it is the control layer only. You keep your own media element, either a plain video or audio tag or a player such as YouTube or HLS.js, and Media Chrome provides the buttons, scrub bar, time display and settings menu as web components you style with CSS.
How do I install Media Chrome?
It is published on npm as media-chrome, at version 4.19.2 in this repository, and the README loads it as an ES module from jsDelivr with a script tag. The package exposes separate entry points for the base elements, the menu, and React wrappers, with both ES module and CommonJS builds.
Does Media Chrome work with React?
Yes, with a caveat the README names: using the custom elements directly in React can be clunky, since you write class= rather than className=. The project publishes React wrapper components for all core elements to avoid that, documented on its React get started page.
What is the difference between Media Chrome and Video.js?
Video.js is a full player with its own media handling and a JavaScript theming API. Media Chrome only replaces the controls, leaves your existing media element in place, and is styled with CSS. That makes it a smaller change if you already have a working streaming stack, and a larger one if you do not.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/muxinc-media-chrome)