feat(react): add createMediaComponent - #2604
Draft
mihar-22 wants to merge 25 commits into
Draft
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
mihar-22
force-pushed
the
pr6b/create-media-component
branch
from
September 3, 2026 00:00
d56298a to
84cab08
Compare
@videojs/cdn
@videojs/core
@videojs/element
@videojs/html
@videojs/media
@videojs/react
@videojs/spf
@videojs/store
@videojs/utils
@videojs/cloudflare-video
@videojs/dash-video
@videojs/hlsjs-video
@videojs/mux-audio
@videojs/mux-video
@videojs/native-hls-video
@videojs/shaka-video
@videojs/spotify-audio
@videojs/tiktok-video
@videojs/twitch-video
@videojs/vimeo-video
@videojs/wistia-video
@videojs/youtube-video
@videojs/google-cast
@videojs/mux-data
commit: |
Contributor
📦 Bundle Size Report🎨 @videojs/html — 30 small size changes
Presets (7)
Media (18)
Players (5)
Skins (29)
UI Components (62)
⚛️ @videojs/react — 24 small size changes
Presets (7)
Media (22)
Extensions (2)
Players (5)
Skins (18)
UI Components (39)
🧩 @videojs/core — no changesEntries (76)
🏷️ @videojs/element — no changesEntries (2)
📦 @videojs/store — no changesEntries (3)
🔧 @videojs/utils — no changesEntries (13)
📦 @videojs/cdn — no changes📦 @videojs/cloudflare-video — no changes📦 @videojs/dash-video — no changes📦 @videojs/google-cast — no changes📦 @videojs/hlsjs-video — no changes📦 @videojs/media — no changesEntries (3)
📦 @videojs/mux — no changes📦 @videojs/mux-audio — no changesEntries (2)
📦 @videojs/mux-data — no changes📦 @videojs/mux-video — no changesEntries (2)
📦 @videojs/native-hls-video — no changes📦 @videojs/shaka-video — no changes📦 @videojs/spf — no changesEntries (7)
📦 @videojs/spotify-audio — no changes📦 @videojs/tiktok-video — no changes📦 @videojs/twitch-video — no changes📦 @videojs/vimeo-video — no changes📦 @videojs/wistia-video — no changesEntries (2)
📦 @videojs/youtube-video — no changesℹ️ How to interpretEach entry is independently bundled, minified, and brotli-compressed. Initial size includes its static import graph; lazy dynamic chunks are reported separately. Entries are not additive because their dependency graphs overlap. Preset rows represent realistic combined bundles. Changes of 300 B or less across initial, lazy, and total size are collapsed, not discarded. Run |
mihar-22
marked this pull request as draft
September 3, 2026 00:04
✅ Deploy Preview for vjs10-site ready!
To edit notification comments on pull requests, go to your Netlify project configuration. |
mihar-22
force-pushed
the
pr6b/create-media-component
branch
from
September 3, 2026 03:19
84cab08 to
7c1d2e6
Compare
mihar-22
force-pushed
the
pr6b/create-media-component
branch
from
September 3, 2026 04:13
7c1d2e6 to
e155a0c
Compare
mihar-22
force-pushed
the
pr6b/create-media-component
branch
from
September 3, 2026 05:34
e155a0c to
46f504b
Compare
mihar-22
force-pushed
the
pr6b/create-media-component
branch
from
September 3, 2026 06:08
46f504b to
fa437f8
Compare
mihar-22
force-pushed
the
pr6b/create-media-component
branch
from
September 3, 2026 06:19
fa437f8 to
3601865
Compare
mihar-22
force-pushed
the
pr6b/create-media-component
branch
from
September 3, 2026 06:31
3601865 to
3e68162
Compare
mihar-22
force-pushed
the
pr6b/create-media-component
branch
from
September 3, 2026 06:40
3e68162 to
b81d177
Compare
mihar-22
marked this pull request as draft
September 7, 2026 04:24
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
The define file exports `SpotifyAudioElement`, so the `endsWith('Audio')`
check has to run on the suffix-stripped name. Adds an `EmbedAudio` fixture
so the iframe-audio path stays covered.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
`HTMLVideoAdapter.host` is `'video' | 'audio'` so the hls.js `MuxAudioAdapter` override still fits, and every other adapter keeps the literal, so façades can read the host off the type as well as the value. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
mihar-22
force-pushed
the
pr6b/create-media-component
branch
from
September 7, 2026 04:28
890a13a to
9c3e27b
Compare
It shadowed the `PlaybackAdapter` interface; the builder fixture stub mirrors the name and types the static `host` it reads. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
mihar-22
force-pushed
the
pr6b/create-media-component
branch
from
September 7, 2026 04:46
9c3e27b to
5e8a81d
Compare
mihar-22
force-pushed
the
pr6b/create-media-component
branch
from
September 7, 2026 05:06
5e8a81d to
3065f9b
Compare
…clared `streamType` is settable on every media adapter, but the DASH and SPF audio adapters left it out of `defaultProps`, so neither `<dash-video stream-type>` nor the React `streamType` prop reached them by contract. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
An adapter declares two statics, `host` and `defaultProps`, and the element
derives everything else from them. Each primitive default becomes a content
attribute of that type, named by its WHATWG spelling or kebab-case, and reset
to the default when removed; objects stay property-only. The base adapters
declare the attributes their native element accepts (`videoContentAttributes`,
`audioContentAttributes`), which pass through to the inner element; an iframe
passes nothing. `static properties` is gone.
`CustomMediaElement(Adapter, { template? })` is now a thin composition of
pieces that stand on their own: `renderHost`, `AdapterAttachment`,
`forwardAdapter` / `reflectAttributes` / `bridgeEvent`, and `MediaChildren`.
Definition state is per class and the light-DOM target lookup only adopts
direct children.
BREAKING CHANGE: `CustomMediaElement` no longer takes a tag or exposes
`static properties`; adapters must declare `static host` and
`static defaultProps`. Audio elements drop the video-only attributes and
embeds reflect only the attributes their adapter declares.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
The builder reads the native attribute lists beside the base adapters and derives custom attributes from each adapter's `defaultProps`, the same rule the element applies, instead of parsing `static properties`. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…eces `<background-video>` renders through `renderHost` and `<hls-background-video>` keeps its engine attached and destroyed through `AdapterAttachment`, instead of each hand-rolling the shadow root and the deferred teardown. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
mihar-22
force-pushed
the
pr6b/create-media-component
branch
from
September 7, 2026 05:49
3065f9b to
75c3a60
Compare
The fixture stub asked for an adapter with `host` and `defaultProps`; the fixture base adapters only had `host` where the builder needed it. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
The `create-media-component` reference lands in #2604, so linking it here broke the site build for this PR alone. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
The html façades follow the UI elements: `media/<name>/element.ts` exports `<Name>Element` carrying its static `tagName`, and the define entry only registers it. `<mux-background-video>` is a subclass of `HlsBackgroundVideoElement` with its own tag, since one class can hold one tag name. BREAKING CHANGE: `HlsJsVideo`, `SpotifyAudio`, and the other html media classes are now `HlsJsVideoElement`, `SpotifyAudioElement`, and so on, and declare their own `tagName`. `@videojs/html/media/<name>` keeps exporting the element class. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
The define entries no longer declare a class, so the builder resolves what they pass to `safeDefine()` to the element class in the media directory and reads its `tagName` and composition there. Fixtures follow the element.ts layout, including a flavour reached through a nested barrel. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
`renderHost(element, attrs)` takes the template and shadow root options from the element's own constructor, so callers stop passing what the class already declares. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Six embeds each carried the same `<style>` and `<iframe>` markup and the same attribute-to-props mapper. `embedTemplate` renders the frame from what differs (URL, feature policy, extra attributes, host sizing, the rule for a host without controls), and `propsFromAttributes` in `@videojs/media/dom` reads the initial attributes through the adapter's `defaultProps`, the same coercion the element applies later. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
`HTMLVideoAdapter.host` was typed `'video' | 'audio'` only so the hls.js `MuxAudioAdapter`, which extends the video adapter, could override it. That made every video adapter's `host` a union in the types. The base is `'video'` again and the Mux audio adapter casts its inverted base to drop the literal, the same way `MuxBackgroundVideoElement` does for `tagName`. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Attributes: - A boolean that defaults to `true` stays property-only. An absent HTML boolean attribute cannot mean `true`, so `<x playsinline>` could never turn it off and `el.playsInline = false` did nothing. - A property from `defaultProps` owns its attribute by definition, so an adapter written with fields rather than accessors works; one exposed read-only only reflects, with a development warning. - `defaultMuted` owns the `muted` attribute, as in HTML, and seeds the `muted` state with it. It used to write `muted` instead, so an embed that stores the two apart built an unmuted URL after `el.defaultMuted = true`. - A number that does not parse falls back to the default. - A target that arrives later gets every attribute already on the element replayed, and only a direct child of the host tag is adopted as `media`. Surface: - Forwarding copies `enumerable`, treats underscore-prefixed members as private, keeps `title` for the element, and reports other collisions in development. - The event bridge forwards composed events an adapter raises itself, skipping only the adapter's copies of events its target dispatched, keeps a stable handler, and is torn down with the adapter. - `MediaChildren` removes an attribute from the clone when it leaves the child, and can disconnect. - `@videojs/media/dom` exports only the deliberate extension points. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
`createMediaComponent(tag, Adapter, options?)` builds a component that renders a native `<video>` or `<audio>`, drives it through one adapter instance, syncs the adapter's props from React props against `Adapter.defaultProps`, forwards everything else (and the ref) to the element, and registers with the Player. The six built-in components that followed that exact shape now use it. The media reference builder reads the target and defaults straight from the factory call, so those components keep their generated React prop tables. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Inferring the element from `attach()` mistyped the SPF adapters, whose mixins take an `HTMLMediaElement`, as audio, and the hls.js `MuxAudioAdapter` as video. The adapter's static `host` names the element directly. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Every adapter's `host` is a literal now, so the non-distributive check for the video union is gone. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What this does
Adds
createMediaComponent, the React façade factory, and builds every React media component with it exceptWistiaVideoandHlsBackgroundVideo.Shape
createMediaComponent(Adapter, render, options?)The render callback receives:
adapterpropschildrenrefinitialPropssrc)Types follow from the adapter alone: the ref and native attributes come from its
attach()target (MediaAdapterTarget), and the adapter props from its staticdefaultProps. No tag parameter, so iframe embeds and the Mux components (with their storyboard child) use the same factory.Also
useAttachMediaaccepts any element target;useAttachIframeis gone.createMediaComponentreference page.Validation
Build, typecheck, lint,
@videojs/reacttests, builder e2e suite, site tests, regenerated media references.🤖 Generated with Claude Code
Note
Medium Risk
Wide refactor across every media adapter and embed URL/mute behavior; public prop types change for several components, though behavior is covered by new tests and existing suites.
Overview
Introduces
createMediaComponent, a shared factory that wires adapter instances into the Player (prop sync viauseSyncProps, attach ref, frozeninitialPropsfor embed URLs) and delegates rendering to a callback.createMediaComponentand related types are exported from@videojs/react.Nearly all built-in React media components (HlsJsVideo, MuxVideo, iframe embeds, etc.) are rewritten to use the factory instead of duplicated
forwardRef+ hook boilerplate;WistiaVideoandHlsBackgroundVideoare unchanged. Component props are nowMediaComponentProps<typeof Adapter>rather than hand-rolled interface merges (notably simplifying Mux variants).useAttachIframeis removed;useAttachMediais generalized to attach any target element (including iframes). The API docs builder detectscreateMediaComponentcalls to infer React target tag and default props; there are unit tests for the factory and an updated reference doc page.Reviewed by Cursor Bugbot for commit 890a13a. Bugbot is set up for automated code reviews on this repo. Configure here.