v10.0.0-beta.32
Compare changes on GitHubThe 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
createPlayerreturns the player element (#2180). The result is nowPlayerElement,PlayerController, andplayerContext: a configured element class you register withcustomElements.define, a controller already bound to that player’s context, and the lower-level context as an escape hatch.ProviderMixin,context, and the standalonecreatestore factory are gone. Where you wroteclass CustomPlayer extends ProviderMixin(UIElement), define the returnedPlayerElementdirectly; where controllers took a context argument, they now take only the host and an optional selector.- Preset and UI registration is explicit (#2247). Preset roots like
@videojs/html/videoare side-effect-free value modules, and registration paths only register:@videojs/html/video/playerdefines the player element,@videojs/html/video/skinregisters the skin, container, and its UI, and@videojs/html/ui/play-buttonregisters only its element. UI registration paths no longer export constructors, so import those from@videojs/html. Media paths such as@videojs/html/media/hls-videoare unchanged: they still export and register. ContainerMixinis nowContainerElement(#2280). Subclass the concrete class instead of composing the mixin, and update the registration import from@videojs/html/media/containerto@videojs/html/ui/container. Themedia-containertag name is unchanged, and attachment now survives elements connecting, disconnecting, reordering, or upgrading late.MediaElementis nowUIElement(#2245). The HTML reactive UI base is renamed so it stops colliding with playback media concepts. ExtendUIElementfor custom controls; playback hosts likeCustomMediaElementkeep their names.- Dialogs split into popup and backdrop parts (#2435).
media-dialog,media-alert-dialog, andmedia-error-dialogaredisplay: contentsstate roots now; dialog semantics, focus registration, and transition completion live on amedia-dialog-popupchild, withmedia-dialog-backdropas an independently styled sibling. In React,Dialog.Rootis provider-only withBackdropandPopupas sibling parts. - Controls split into content and backdrop parts (#2436). React
Controls.Rootis provider-only: move DOM props, refs, and styling ontoControls.Content. In HTML,media-controlshosts context whilemedia-controls-contentcarries the layout, next to the optionalmedia-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). 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 and React, and Dialog has reference pages with player-modal demos for HTML and React (#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), menus separated their popup from their content (#2347), dialogs and controls made the same split (breaking, above), and the Default and Minimal skins were aligned so CSS and Tailwind render the same structure, including reduced-motion and forced-colors behavior (#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). 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 and React. 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), the Minimal audio skins got their volume controls back (#2386), and audio playback rate controls gained tooltips (#2389, #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). 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): autoplay for HTML and React, live streams for HTML and React, remembering user preferences, error handling, casting, seek previews, and more. A background video guide followed for HTML and React (#2339), plus Vue and Svelte integration guides (#2333, #2374).
Reference coverage widened too:
- Component event tables are generated from the source, the same way engine options already were (#2404).
- Hotkey (#2405) and Gesture (#2406) pages, for HTML and React.
- SeekIndicator, StatusAnnouncer, StatusIndicator, and VolumeIndicator pages (#2416, #2417, #2418, #2419).
- Native Video and Audio hosts (#2420),
useContainer(#2421), and missing utilities (#2422). - Browser and tooling compatibility is documented in Browser support (#2330), the skin customization model has a proper explanation (#2315), and the custom element lifecycle has its own concept page (#2424).
- A stack of smaller corrections and polish across menus, sliders, containers, tooltips, migrations, and high-traffic JSDoc (#2240, #2298, #2326, #2328, #2329, #2332, #2334, #2335, #2338, #2385, #2402, #2407, #2408, #2409, #2410, #2411, #2412, #2413, #2414, #2415, #2423, #2425, #2430).
Assorted fixes
- Anchored popovers stay attached to their triggers while the page scrolls (#2387).
- Menus size to their available space, keep item labels on one line, and clear the parent highlight while a submenu is open (#2440).
- Registered translation overrides survive re-registration (#2354), and durations format through
Intl.NumberFormatandIntl.ListFormatinstead ofIntl.DurationFormat, which broadens browser support (#2336). @videojs/htmland engine entries import cleanly on the server, so SSR frameworks can share modules with the client (#2428, #2429).- Props set on an element before its definition registers survive the late upgrade (#2400), and popup roots handle being detached (#2348).
- Skin-internal CSS custom properties carry a prefix so they can’t collide with yours (#2391).
- The vjsc skin pipeline reports style diagnostics (#2345), hardens its Vite workflow (#2355), and matches packaged skin visuals again (#2344).
Thanks to @dylanjha for a first contribution: the how-to guide series in #1945.