Aug 26, 2026•Breaking changes

# v10.0.0-beta.32

[Compare changes on GitHub](https://github.com/videojs/v10/compare/@videojs/core@10.0.0-beta.31...@videojs/core@10.0.0-beta.32)

The player’s chrome grew a full anatomy: dialogs, controls, menus, and their backdrops are now separate parts with their own state attributes, so you can style and animate each surface without selector gymnastics. A new Dialog component puts your own content in a modal over the player, and a set of task-based how-to guides landed alongside a wave of reference pages. Six breaking changes ship with it, all in the HTML package’s public API or the shared component anatomy.

## Breaking changes

*   **`createPlayer` returns the player element** ([#2180](https://github.com/videojs/v10/pull/2180)). The result is now `PlayerElement`, `PlayerController`, and `playerContext`: a configured element class you register with `customElements.define`, a controller already bound to that player’s context, and the lower-level context as an escape hatch. `ProviderMixin`, `context`, and the standalone `create` store factory are gone. Where you wrote `class CustomPlayer extends ProviderMixin(UIElement)`, define the returned `PlayerElement` directly; where controllers took a context argument, they now take only the host and an optional selector.
*   **Preset and UI registration is explicit** ([#2247](https://github.com/videojs/v10/pull/2247)). Preset roots like `@videojs/html/video` are side-effect-free value modules, and registration paths only register: `@videojs/html/video/player` defines the player element, `@videojs/html/video/skin` registers the skin, container, and its UI, and `@videojs/html/ui/play-button` registers only its element. UI registration paths no longer export constructors, so import those from `@videojs/html`. Media paths such as `@videojs/html/media/hls-video` are unchanged: they still export and register.
*   **`ContainerMixin` is now `ContainerElement`** ([#2280](https://github.com/videojs/v10/pull/2280)). Subclass the concrete class instead of composing the mixin, and update the registration import from `@videojs/html/media/container` to `@videojs/html/ui/container`. The `media-container` tag name is unchanged, and attachment now survives elements connecting, disconnecting, reordering, or upgrading late.
*   **`MediaElement` is now `UIElement`** ([#2245](https://github.com/videojs/v10/pull/2245)). The HTML reactive UI base is renamed so it stops colliding with playback media concepts. Extend `UIElement` for custom controls; playback hosts like `CustomMediaElement` keep their names.
*   **Dialogs split into popup and backdrop parts** ([#2435](https://github.com/videojs/v10/pull/2435)). `media-dialog`, `media-alert-dialog`, and `media-error-dialog` are `display: contents` state roots now; dialog semantics, focus registration, and transition completion live on a `media-dialog-popup` child, with `media-dialog-backdrop` as an independently styled sibling. In React, `Dialog.Root` is provider-only with `Backdrop` and `Popup` as sibling parts.
*   **Controls split into content and backdrop parts** ([#2436](https://github.com/videojs/v10/pull/2436)). React `Controls.Root` is provider-only: move DOM props, refs, and styling onto `Controls.Content`. In HTML, `media-controls` hosts context while `media-controls-content` carries the layout, next to the optional `media-controls-backdrop`.

## A real anatomy for player chrome

Dialog is a generic modal for your own content, like opening a player from a thumbnail ([#2379](https://github.com/videojs/v10/pull/2379)). It handles focus trapping and restoration, background isolation, transitions, trigger wiring, and Escape dismissal, and dismissal stays explicit so a play button inside a dialog doesn’t close it. AlertDialog and ErrorDialog now compose the same primitives while keeping their specialized semantics, the Video.js 8 `ModalDialog` migration path is documented in the migration guide for [HTML](/docs/framework/html/how-to/migrate-from-video-js-8/) and [React](/docs/framework/react/how-to/migrate-from-video-js-8/), and Dialog has reference pages with player-modal demos for [HTML](/docs/framework/html/reference/dialog/) and [React](/docs/framework/react/reference/dialog/) ([#2375](https://github.com/videojs/v10/pull/2375)).

The rest of the chrome follows the same shape: state owners are renderless, and visual surfaces are explicit sibling parts. Backdrops arrived as optional anatomy for alert dialogs and controls ([#2343](https://github.com/videojs/v10/pull/2343)), menus separated their popup from their content ([#2347](https://github.com/videojs/v10/pull/2347)), dialogs and controls made the same split ([breaking, above](#breaking-changes)), and the Default and Minimal skins were aligned so CSS and Tailwind render the same structure, including reduced-motion and forced-colors behavior ([#2437](https://github.com/videojs/v10/pull/2437)). A backdrop receives the same transition state attributes as its surface, so a scrim and its content can animate on independent curves without cross-component selectors.

## Volume UI that respects the device

`VolumePopover` is a new compound of Root, Trigger, and Popup parts that follows volume availability instead of skin heuristics ([#2378](https://github.com/videojs/v10/pull/2378)). On devices that don’t expose volume control, it keeps the mute button, drops the popup and its ARIA wiring, and closes an open popup if availability changes mid-session. Read the reference for [HTML](/docs/framework/html/reference/volume-popover/) and [React](/docs/framework/react/reference/volume-popover/). Nearby: the container now reflects controls visibility through `data-controls-visible`, so skins key cursor, captions offset, and transition timing off the owning boundary ([#2376](https://github.com/videojs/v10/pull/2376)), the Minimal audio skins got their volume controls back ([#2386](https://github.com/videojs/v10/pull/2386)), and audio playback rate controls gained tooltips ([#2389](https://github.com/videojs/v10/pull/2389), [#2388](https://github.com/videojs/v10/pull/2388)).

## Playback stays in one codec family

On mixed-codec HLS sources, the playback engine now keeps rendition selection within the codec family of the initial pick ([#2289](https://github.com/videojs/v10/pull/2289)). The engine implements no `SourceBuffer.changeType()`, so a mid-stream hop from AVC to HEVC appended undecodable data; now the initial pick prefers AVC and AAC by default, later picks stay in that family, and a cross-family user pick is ignored instead of killing playback. Configure or disable the preference with `preferredCodecs`. The same PR fixed a signals-engine bug where an effect that writes through its own dependencies stopped seeing later changes.

## Guides and reference coverage

A set of task-oriented how-to guides landed, each solving one job with copy-pasteable code for HTML and React, and every example was QA’d by running it in a real browser ([#1945](https://github.com/videojs/v10/pull/1945)): autoplay for [HTML](/docs/framework/html/how-to/autoplay/) and [React](/docs/framework/react/how-to/autoplay/), live streams for [HTML](/docs/framework/html/how-to/play-live-streams/) and [React](/docs/framework/react/how-to/play-live-streams/), remembering user preferences, error handling, casting, seek previews, and more. A background video guide followed for [HTML](/docs/framework/html/how-to/add-a-background-video/) and [React](/docs/framework/react/how-to/add-a-background-video/) ([#2339](https://github.com/videojs/v10/pull/2339)), plus Vue and Svelte integration guides ([#2333](https://github.com/videojs/v10/pull/2333), [#2374](https://github.com/videojs/v10/pull/2374)).

Reference coverage widened too:

*   Component event tables are generated from the source, the same way engine options already were ([#2404](https://github.com/videojs/v10/pull/2404)).
*   Hotkey ([#2405](https://github.com/videojs/v10/pull/2405)) and Gesture ([#2406](https://github.com/videojs/v10/pull/2406)) pages, for [HTML](/docs/framework/html/reference/hotkey/) and [React](/docs/framework/react/reference/hotkey/).
*   SeekIndicator, StatusAnnouncer, StatusIndicator, and VolumeIndicator pages ([#2416](https://github.com/videojs/v10/pull/2416), [#2417](https://github.com/videojs/v10/pull/2417), [#2418](https://github.com/videojs/v10/pull/2418), [#2419](https://github.com/videojs/v10/pull/2419)).
*   Native Video and Audio hosts ([#2420](https://github.com/videojs/v10/pull/2420)), `useContainer` ([#2421](https://github.com/videojs/v10/pull/2421)), and missing utilities ([#2422](https://github.com/videojs/v10/pull/2422)).
*   Browser and tooling compatibility is documented in [Browser support](/docs/framework/html/concepts/browser-support/) ([#2330](https://github.com/videojs/v10/pull/2330)), the skin customization model has a proper explanation ([#2315](https://github.com/videojs/v10/pull/2315)), and the custom element lifecycle has its own concept page ([#2424](https://github.com/videojs/v10/pull/2424)).
*   A stack of smaller corrections and polish across menus, sliders, containers, tooltips, migrations, and high-traffic JSDoc ([#2240](https://github.com/videojs/v10/pull/2240), [#2298](https://github.com/videojs/v10/pull/2298), [#2326](https://github.com/videojs/v10/pull/2326), [#2328](https://github.com/videojs/v10/pull/2328), [#2329](https://github.com/videojs/v10/pull/2329), [#2332](https://github.com/videojs/v10/pull/2332), [#2334](https://github.com/videojs/v10/pull/2334), [#2335](https://github.com/videojs/v10/pull/2335), [#2338](https://github.com/videojs/v10/pull/2338), [#2385](https://github.com/videojs/v10/pull/2385), [#2402](https://github.com/videojs/v10/pull/2402), [#2407](https://github.com/videojs/v10/pull/2407), [#2408](https://github.com/videojs/v10/pull/2408), [#2409](https://github.com/videojs/v10/pull/2409), [#2410](https://github.com/videojs/v10/pull/2410), [#2411](https://github.com/videojs/v10/pull/2411), [#2412](https://github.com/videojs/v10/pull/2412), [#2413](https://github.com/videojs/v10/pull/2413), [#2414](https://github.com/videojs/v10/pull/2414), [#2415](https://github.com/videojs/v10/pull/2415), [#2423](https://github.com/videojs/v10/pull/2423), [#2425](https://github.com/videojs/v10/pull/2425), [#2430](https://github.com/videojs/v10/pull/2430)).

## Assorted fixes

*   Anchored popovers stay attached to their triggers while the page scrolls ([#2387](https://github.com/videojs/v10/pull/2387)).
*   Menus size to their available space, keep item labels on one line, and clear the parent highlight while a submenu is open ([#2440](https://github.com/videojs/v10/pull/2440)).
*   Registered translation overrides survive re-registration ([#2354](https://github.com/videojs/v10/pull/2354)), and durations format through `Intl.NumberFormat` and `Intl.ListFormat` instead of `Intl.DurationFormat`, which broadens browser support ([#2336](https://github.com/videojs/v10/pull/2336)).
*   `@videojs/html` and engine entries import cleanly on the server, so SSR frameworks can share modules with the client ([#2428](https://github.com/videojs/v10/pull/2428), [#2429](https://github.com/videojs/v10/pull/2429)).
*   Props set on an element before its definition registers survive the late upgrade ([#2400](https://github.com/videojs/v10/pull/2400)), and popup roots handle being detached ([#2348](https://github.com/videojs/v10/pull/2348)).
*   Skin-internal CSS custom properties carry a prefix so they can’t collide with yours ([#2391](https://github.com/videojs/v10/pull/2391)).
*   The vjsc skin pipeline reports style diagnostics ([#2345](https://github.com/videojs/v10/pull/2345)), hardens its Vite workflow ([#2355](https://github.com/videojs/v10/pull/2355)), and matches packaged skin visuals again ([#2344](https://github.com/videojs/v10/pull/2344)).

Thanks to [@dylanjha](https://github.com/dylanjha) for a first contribution: the how-to guide series in [#1945](https://github.com/videojs/v10/pull/1945).

---

All documentation: https://videojs.org/llms.txt