LmCast :: Stay tuned in

Show HN: I took back Video.js after 16 years and we rewrote it to be 88% smaller

Recorded: March 25, 2026, 3 a.m.

Original Summarized

Video.js v10 Beta: Hello, World (again) | Blog | Video.js | Open Source Video Player
Using Video.js v8 or earlier?
Visit legacy.videojs.org
Search
Docs
Blog Discord GitHub Menu Mar 10, 2026 • 14 min read Video.js v10 Beta: Hello, World (again)
By Steve Heffernan Today we’re excited to release the Video.js v10.0.0 beta. It’s the result of a rather large ground-up rewrite, not just of Video.js (discussion) but also of Plyr, Vidstack, and Media Chrome, through a rare teaming-up of open source projects and people who care a lot about web video, with a combined 75,000 github stars and tens of billions of video plays monthly.
I built Video.js 16 years ago to help the transition from Flash to HTML5 video. It’s grown a lot since then with the help of many people, but the codebase and APIs have continued to reflect a different era of web development. This rebuild modernizes the player both for how developers build today and sets up the foundation for the next significant transition to AI-augmented features and development.
We’ve focused on:

Shrinking bundle sizes, and then shrinking them more (88% reduction in default bundle size)
Allowing deep customization using the familiar development patterns of your chosen framework — including new first-class React, Typescript, and Tailwind support
Making the defaults look beautiful and perform beautifully (The experts are calling me saying “Sir, how did you make it so great?”. It’s incredible, really.)
Designing the codebase and docs so AI agents building your player alongside you can actually be good at it

We’re pretty sure it works differently from what you’ve come to expect of a web media player, while we hope it feels more familiar to how you actually build.
HTML
htmlhtml<video-player>
<video-skin>
<video src="video.mp4"></video>
</video-skin>
</video-player>
React
tsxtsximport '@videojs/react/video/skin.css';

import { createPlayer } from '@videojs/react';
import { videoFeatures, VideoSkin, Video } from '@videojs/react/video';

const Player = createPlayer({
features: videoFeatures,
});

function App() {
return (
<Player.Provider>
<VideoSkin>
<Video src="video.mp4" />
</VideoSkin>
</Player.Provider>
);
}
<small>Bundle</small>
One of the biggest complaints about video players today is their file size, often weighing in around 1MB minified and hundreds of KB gzipped. Players are sneakily-complex applications so there’s only so many bytes you can shave off, but legacy players were built in times before smart bundlers, tree shaking, and other size-saving opportunities. They carry with them many features you may not be actively using.
The Video.js v10 default player is now 88% smaller than the size of the previous version’s (v8.x.x) default. A good chunk of those savings come from the decision to unbundle adaptive bitrate (ABR) support, which you could remove in the previous version by instead importing from video.js/core , but the majority of video.js installs just use the default bundle while also not using the adaptive streaming features. Comparing more similar apples, with ABR removed, the v10 default video player (HTML) is still 66% smaller than the size of the previous version, getting even smaller from there depending on which bundle you need.

player minified (kB) gzip (kB) notes Video.js v8 (core) 260.5 75.2 Vidstack 237.4 74.1 Media Chrome 175.5 41.3 Plyr 109.8 32.6 Video.js v10 Video Player [HTML] 97.4 25.1 Video.js v10 Audio Player [HTML] 85.8 23.0 Video.js v10 Video Player [React] 62.0 18.0 Video.js v10 Audio Player [React] 49.2 15.2 Video.js v10 Background Video [HTML] 22.2 6.9 Video.js v10 Background Video [React] 10.7 3.5
v10 Engine (vroooom)
While the previous section was comparing players without ABR, a lot of the weight of a fully-featured video player comes from the streaming engine which is needed to handle adaptive bitrate (ABR) formats like HLS and DASH — for manifest parsing, segment loading, buffer management, ABR logic, codec detection, MSE integration, DRM, server-side ads, and more. Similar to players, traditional streaming engines have monolithic architectures making it difficult to get the bundle size smaller.
As part of v10 we’ve started a new engine project called SPF 😎 (Streaming Processor Framework), which is a framework built around functional components that are composed into purpose-built, smaller streaming engines. For example if you have a short-form video app with simple adaptive streaming needs, your engine won’t ship with any code for DRM and ads.
For a simple HLS use case, Video.js v10 using SPF is only 19% the file size of Video.js v8 including adaptive bitrate streaming (ABR).

player minified (kB) gzip (kB) notes Vidstack + HLS.js 764.3 238.1 Media Chrome + HLS.js 701.2 202.9 Video.js v8 + VHS* 697.0 202.7 *VHS is bundled by default Plyr + HLS.js 614.0 188.5 Video.js v10 + HLS.js 526.5 164.1 Video.js v10 + SPF [HTML] 144.6 38.7 Video.js v10 + SPF [React] 107.3 31.6 v10 Background Video + SPF [HTML] 61.2 18.9 v10 Background Video + SPF [React] 49.2 15.6
Comparing engines to engines you get a clearer picture of the story. The other engines are very difficult to get any smaller without forking them, while the engine composed using SPF only includes what’s needed for simple adaptive streaming using HLS, making it only 12% the file size of even HLS.js-light .

engine minified (kB) gzip (kB) notes v10 compatible dash.js 962.1 294.2 DASH only ✅ Shaka 753.0 239.1 HLS and DASH support ✅ hls.js 503.4 155.9 HLS only ✅ VHS (v8) * 434.4 127.6 * Requires video.js; not standalone. HLS and DASH support ✅ Shaka (custom build **) 350.0 115.0 ** Shaka can be made smaller with a Closure Compiler custom build, not an npm import. This build size targets “simple” ABR. ✅ HLS.js-light 328.5 103.4 Removes DRM, subs, alt-audio, CMCD, interstitials ✅ SPF-composed engine 38.5 12.1 Includes only what’s needed for “simple” ABR ✅
To be clear, the immediate goal isn’t for SPF to replace the full-featured engines like HLS.js for advanced streaming use cases, and in fact v10 works with all these streaming engines today. The goal is to achieve much smaller file sizes for common, simpler use cases. At the same time we think a lot more sites and apps could benefit from simple ABR, and we want SPF to lower the file size cost of using it.
But wait, there’s more!
With v10 the file size story doesn’t actually start with the baseline builds. The library is built for composing a player with only what’s needed, allowing for simple use cases to be even smaller.
For example here’s a simple React “hello world” with just a video and play button, weighing in at <5 kB gzipped .
tsxtsximport { createPlayer, features } from '@videojs/react';
import { Video } from '@videojs/react/video';

const Player = createPlayer({
features: [features.playback],
});

function App() {
const store = Player.usePlayer();
const paused = Player.usePlayer((s) => s.paused);

return (
<Player.Provider>
<Player.Container>
<Video src="video.mp4" />
<button onClick={() => (paused ? store.play() : store.pause())}>
{paused ? 'Play' : 'Pause'}
</button>
</Player.Container>
</Player.Provider>
);
}
You could for sure build that example with an even smaller file size, but it’s meant to show that the the player infrastructure is minimal, while supporting much more advanced and custom players.
In v10 we first split State, UI, and Media into their own components that work together through API contracts instead of monolithic controllers and overloaded player objects. Each major component is optional and easily swappable or configurable. UI and Media components can also be used just by themselves.
The createPlayer function takes an array of features (like Zustand store slices) to build up its internal state capabilities. If your player doesn’t need audio it doesn’t have to bundle the code for volume and mute. In legacy players this wasn’t possible without forking the code.
Don’t need UI or want to build your own? Just delete the skin, it’s right there in your code. In legacy players, setting controls=false still results in a bundle with all the controls. With v10 if you don’t import a component, it doesn’t exist in your bundle.
File size is far from the only important performance metric when it comes to video players, but it’s one that can get away from you quickly if you don’t architect for it upfront. There’s still improvements we can make but we’re really happy with the results of the new architecture so far.
Player UI customization you might actually enjoy
Video.js v10 beta comes with a few polished, complete skins (control sets) you can use out of the box. But we hope you don’t stop there because we’ve put a lot of effort into making the UI components themselves great to work with in any framework. We’ve started with React and Web Components, but hope to move quickly into supporting other popular JS frameworks directly.
When you’re ready to go deeper, you can eject any skin and get the full source code in your framework’s language — real components you own and modify, inspired by shadcn/ui. For Beta “eject” just means copy/paste from the docs, but a fancier CLI option is on the way.
v10’s UI is built with unstyled UI primitives, inspired by libs like Base UI and Radix, which means they get out of your way when you’re trying to do anything custom. Each component outputs a single HTML element, so you have direct access to everything happening in the UI.
React
tsxtsx<TimeSlider.Root className="slider">
<TimeSlider.Track className="slider-track">
<TimeSlider.Buffer className="slider-buffer" />
<TimeSlider.Fill className="slider-fill" />
</TimeSlider.Track>
<TimeSlider.Thumb className="slider-thumb" />
</TimeSlider.Root>
HTML
htmlhtml<media-time-slider class="slider">
<media-slider-track class="slider-track">
<media-slider-buffer class="slider-buffer"></media-slider-buffer>
<media-slider-fill class="slider-fill"></media-slider-fill>
</media-slider-track>
<media-slider-thumb class="slider-thumb"></media-slider-thumb>
</media-time-slider>
They’re more verbose, and as a long time HTML-er I’ll admit I was not a fan at first glance. But after building a player skin with them I understood why this is the way . For example, in the previous version (v8) the timeline thumb/handle was a pseudo-element on a nested child. You overrode it through inspecting the player’s output, using specificity and a font-size for dimensions.
csscss.video-js .vjs-play-progress:before {
font-size: 0.9em;
background: white;
border-radius: 50%;
z-index: 1;
}
In v10, it’s a real element with a class you control.
csscss.slider-thumb {
width: 0.75rem;
height: 0.75rem;
background: white;
border-radius: 50%;
}
Design
The previous version’s default skin is used billions of times every month, and yet we put relatively little design effort into it. At the time I hoped devs would style it and make it their own, and then they didn’t.
For v10, Sam Potts (creator of Plyr, 29,000 GitHub stars largely on the strength of its design) designed the new skins, and will continue to invest in them and iterations over time. The beta ships two skins: a default skin with a frosted aesthetic and a minimal skin for developers who want a clean starting point, both with refined controls, smooth interactions, and thoughtful animations
Default skins theme (VideoSkin and AudioSkin)


Minimal skins theme (MinimalVideoSkin and MinimalAudioSkin)


One detail I love is the error dialog, where the visual treatment matches the skin. I’m sure that feels tiny and simple, but in Video.js history this level of detail was so far down the priority list that for a decade the error dialog has been my big ugly text ‘X’, for every skin. So when I see these new error dialogs it helps confirm we’re all setting the bar higher, and I’m loving it.
Default

Minimal

Amazon.com featuring the version 8 error dialog “X” (I forced the error for the screenshot)

(Amazon, I can help you upgrade! You have all of my contact info.)
Presets for use cases (video, audio, background)
While these beta skins are a great starting point, they’re just the beginning.
If you wanted to build a podcast player with Video.js v8, you’d start with a video player, strip out the video-specific parts, add some specific audio features, and then spend real time on UI customization to get something that actually looked and felt like a podcast player. Same story for a background video on a landing page, or a short-form swipeable player, or a classroom course player.
We do actually know what people are building, believe it or not. Not just the individual features, but the specific combinations that tend to show up together. A TV streaming app needs different things than a hero background video, which needs different things than a podcast player. And those combinations are pretty consistent across the web.
So in v10 we’re packaging them up as presets. A preset is a purpose-built combination of skin, features, and media configuration for a specific use case. Instead of assembling a player from scratch, you’ll pick the preset closest to what you’re building and start there.
The beta ships three:
A default video preset (general website video, the kind of thing you might otherwise use the HTML video tag for)
htmlhtml<video-player>
<video-skin>
<video src="video.mp4"></video>
</video-skin>
</video-player>
jsxjsxconst Player = createPlayer({
features: videoFeatures,
});

function App() {
return (
<Player.Provider>
<VideoSkin>
<Video src="video.mp4" />
</VideoSkin>
</Player.Provider>
);
}
A default audio preset (same idea but for the audio tag)
htmlhtml<audio-player>
<audio-skin>
<audio src="audio.mp3"></audio>
</audio-skin>
</audio-player>
jsxjsxconst Player = createPlayer({
features: audioFeatures,
});

function App() {
return (
<Player.Provider>
<AudioSkin>
<Audio src="audio.mp3" />
</AudioSkin>
</Player.Provider>
);
}
And a background video preset. Background video is where this concept really starts to click, because a background video needs layout but doesn’t need controls and doesn’t need audio. Rather than handing you a full player and asking you to remove things, we just give you the right player for the job.
htmlhtml<background-video-player>
<background-video-skin>
<background-video></background-video>
</background-video-skin>
</background-video-player>
jsxjsxconst Player = createPlayer({
features: backgroundVideoFeatures,
});

function App() {
return (
<Player.Provider>
<BackgroundVideoSkin>
<BackgroundVideo src="video.mp4" />
</BackgroundVideoSkin>
</Player.Provider>
);
}
This is also where the compositional architecture pays off. The preset gets you started fast. The composable foundation underneath means you can still add, remove, or replace anything. You get a real starting point without giving up any of the flexibility.
Over time we’ll expand into more use cases: creator-platform players, short-form swipeable video, educational course players. If there’s a use case you’d love to see, let us know.
How we’re thinking about AI
The last year, and even just the last few months specifically have been a wild time to be building a new project like this. We’re of course excited for how AI will create interesting interactive player features in the months and years to come and we have a few ideas of what those will be. For Beta, however, we’ve been focused on the agent experience of building video.js-based players with the help of AI.

Less-abstracted components and unstyled UI primitives so agents can do more with the code right in your project and need fewer external docs
llms.txt to make navigating Video.js docs more efficient, including framework-specific llms.txt
Markdown versions of every individual doc. If your agent hits our site with the accept: text/markdown header — as many like Claude Code do — we’ll send the markdown version of the page, saving your agent loads of unnecessary context bloat.
A growing set of AI skills in the repo, currently helping us build and soon will help you build too.

In writing this part and getting input from the team I found we actually have a lot to say about our experience building with AI and the many ways we put it to use, so keep an eye out for a followup post on that topic.
Try the beta
A few things to know:

The APIs are not quite stable. This is a beta, so some interfaces will change before GA. Build with it, experiment, give us feedback, put it on simple projects. It’s not yet the time to do a major migration.
The features may be limited. You might be surprised by which features aren’t supported yet and also by which already are. We are building from a base of four existing players, but our goal for reaching beta was simple website playback functionality. It’s accessible and supports captions, and already works with many formats and streaming services, but things like settings menus are still on their way.
We really want your feedback. File issues on GitHub, join the conversation on discord, tell us what works and what doesn’t. In general, people seem to get less engaged with JS “widgets” like a video player compared to JS frameworks, so please don’t be shy. Your input is really valuable.

If you’re starting something new, this is a good time to try v10. Go to videojs.org and check out the installation guide.
If you’re running a previous version in production, sit tight. We’ll have migration guides before we ask you to move.
What’s coming
We’re aiming for mid-2026 for GA. Between now and then:

Feature parity with the capabilities developers rely on in the previous version of Plyr, Vidstack and Media Chrome.

Planning ads support later in 2026. Ads are complicated.

Migration guides for Video.js v8, Plyr, Vidstack, and Media Chrome
More player presets for common use cases

Big thanks to…
@cpilsbury, @decepulis, @esbie, @luwes, @mihar-22, @sampotts for building the thing — who needs AI when you have the absolute best team of people in the world. I’m aware that makes no sense.
That said, @claude. I don’t know if you can hear this yet, but we certainly burned through some tokens together.
@dhassoun, @essk, @ewelch-mux, @gesinger, @gkatsev, @kixelated, @littlespex, @mister-ben, @misteroneill for being the best advisors and internal champions a project could hope for.
My company @muxinc for stepping in to make sure Video.js still has breath and allowing many of us to spend all our time on it. And @brightcove for keeping it breathing for so many years before. Our friends at @qualabs for carrying the load of other projects and giving us time to focus.

I’m very excited for you to fall back in love with your video player ❤️ (This is a theme we’re doing. We have cool stickers.)
Cheers!
@heff
Docs
Blog Privacy V8 Discord GitHub
Video.js is a free and open source HTML5 video player.
Video hosting and streaming sponsored by Mux
The term VIDEO.JS is a registered trademark of Brightcove
Inc.

© 2010–2026 Video.js contributors

Video.js v10 Beta: Hello, World (again) – A Comprehensive Summary

Video.js v10 Beta represents a substantial, ground-up rewrite of the platform, extending beyond just the Video.js player itself to encompass Plyr, Vidstack, and Media Chrome, all driven by a community of over 75,000 GitHub contributors and supporting tens of billions of video plays monthly. Steve Heffernan initiated Project Video.js sixteen years ago to facilitate the transition from Flash to HTML5 video, and this new iteration aims to modernize the player for contemporary development practices, while simultaneously laying the groundwork for future AI-augmented features. The primary objectives of this rebuild are threefold: significantly reducing bundle sizes, providing deep customization options integrated with popular frameworks, and designing a codebase and documentation framework that allows for AI agent assistance in player creation.

A key focus has been on minimizing bundle sizes, with a remarkable 88% reduction achieved in the default bundle compared to Video.js v8.x.x. This reduction is largely attributed to the decision to unbundle adaptive bitrate (ABR) support. Previously, importing ABR support was required from Video.js/core, but in v10, it’s now integrated. The default player’s size is now 66% smaller than version 8, depending on the selected bundle. The specific measurements of various player configurations (core, Vidstack, Media Chrome, Plyr) are provided, demonstrating substantial file size reductions for HTML, React, and background video implementations. These include: HTML Video Player (97.4 kB minified, 25.1 kB gzipped), HTML Audio Player (85.8 kB minified, 23.0 kB gzipped), React Video Player (62.0 kB minified, 18.0 kB gzipped), React Audio Player (49.2 kB minified, 15.2 kB gzipped), and Background Video Player (22.2 kB minified, 6.9 kB gzipped).

The underlying backbone of this change is the introduction of the ‘SPF’ (Streaming Processor Framework), a new streaming engine designed for modularity. Unlike traditional monolithic streaming engines, SPF uses functional components, enabling a more targeted approach to ABR formats like HLS and DASH. This architecture allows for significantly smaller file sizes; for instance, using SPF with HLS.js, the v10 player is only 19% the size of Video.js v8, inclusive of ABR. A comparison of different engines highlights this trend, with SPF-composed engine reaching just 12% the size of even HLS.js-light. Engine sizes (minified, gzipped) include: dash.js (962.1 kB, 294.2 kB), Shaka (753.0 kB, 239.1 kB), hls.js (503.4 kB, 155.9 kB), VHS (434.4 kB, 127.6 kB), Shaka (custom build – 350.0kB, 115.0 kB), HLS.js-light (328.5 kB, 103.4 kB), and the SPF-composed engine (38.5 kB, 12.1 kB).

The development team emphasizes that the immediate goal isn’t to replace existing engines entirely, particularly for advanced use cases. The v10 player remains compatible with existing engines like HLS.js. However, the SPF engine’s efficiency makes it ideal for simpler ABR configurations. Furthermore, the revamp includes a redesigned UI, built using unstyled UI primitives inspired by libraries such as Base UI and Radix. This design approach aims to minimize bundle size and provide developers with maximum control over UI customization. The new UI components, available in both HTML and React formats, offer direct access to UI elements, facilitating a more efficient workflow.

The development focuses on composing a player with only the necessary features. This approach allows for the creation of simplified players for use cases such as background videos on landing pages or short-form swipeable video players. To illustrate this, a simple React "hello world" player is presented with a gzipped file size of under 5 kB—demonstrating the potential for lightweight design. A significant shift is the splitting of state, UI, and media components into distinct, API-driven modules, increasing modularity and flexibility. The rearchitecting makes the player more adaptable and easier to maintain.

The Beta release includes pre-built skins for quick deployments, but developers are also offered full source code access for customization through “ejecting” the skins, mirroring the “shadcn/ui” approach. The team has created presets tailored for common use cases – a default video preset, a default audio preset, and a background video preset – streamlining the development process. Steve Heffernan, utilizing a large team, aims to build and incorporate AI features into the platform. The team is also gathering data on how developers are combining features and use cases to inform future development.