Skip to content

feat(packages): add text track utilities - #2439

Closed
mihar-22 wants to merge 3 commits into
mainfrom
t3code/add-text-track-hooks
Closed

feat(packages): add text track utilities#2439
mihar-22 wants to merge 3 commits into
mainfrom
t3code/add-text-track-hooks

Conversation

@mihar-22

@mihar-22 mihar-22 commented Aug 26, 2026

Copy link
Copy Markdown
Member

Summary

  • add lifecycle-safe text-track creation and observation primitives shared by the HTML and React bindings
  • expose four focused React hooks and one HTML reactive controller, following Vidstack's useful create/active/cue split while fitting Video.js media context and controller lifecycles
  • keep URL-backed tracks native and document the complete framework API surface

Native media uses real <track> children for programmatic tracks, which makes cleanup possible. Non-native media can implement the optional removeTextTrack() capability. Active-track selection prefers showing, falls back to hidden, and ignores disabled; this keeps chapter and metadata tracks active without rendering them.

API surface

React (@videojs/react)

API Result Behavior
useCreateTextTrack(options) CreatedTextTrack | null Creates a track for the current media and removes it when the component unmounts, the options change, or the media changes. Returns { track, addCue, removeCue }; write cues through those so observers refresh and writes wait for the native <track> to load.
useActiveTextTrack(kind) TextTrackLike | null Observes the enabled track matching one kind or a readonly array of kinds.
useTextCues(track) TextCueLike[] Returns a fresh snapshot of every cue on the track.
useActiveTextCues(track) TextCueLike[] Returns a fresh snapshot of cues active at the current playback position.

useCreateTextTrack accepts:

interface UseTextTrackOptions {
  kind: 'subtitles' | 'captions' | 'descriptions' | 'chapters' | 'metadata';
  label?: string;
  language?: string;
  mode?: 'showing' | 'hidden' | 'disabled'; // defaults to hidden
}

HTML (@videojs/html)

TextTrackController has two source modes:

new TextTrackController(host, {
  kind: 'metadata',
  label: 'ad-cues',
});

new TextTrackController(host, ['captions', 'subtitles']);

The options overload owns a created track. The kind overload observes an existing active track. Both expose:

Member Purpose
.value Current created or active track.
.cues Snapshot of every cue on the current track.
.activeCues Snapshot of cues active at the current playback position.
.addCue(cue) Add a cue and refresh both snapshots.
.removeCue(cue) Remove a cue and refresh both snapshots.

The controller follows the media context, reconnects when the player changes media, and owns all subscriptions and cleanup.

Shared DOM primitives (@videojs/media/dom)

  • createTextTrack() returns a { track, addCue, removeCue, destroy } handle whose cue writes notify observers and wait for the backing <track> to load
  • addTextTrackCue() and removeTextTrackCue() write cues on any track and notify observers
  • createTextTrackElement() creates the removable native backing element
  • getActiveTextTrack() and watchActiveTextTrack() select and observe enabled tracks
  • getTextTrackCues() and watchTextTrackCues() read and observe cue snapshots
  • CreateTextTrackOptions, TextTrackHandle, and TextTrackKindFilter describe the shared contracts

The framework-neutral media contracts now model optional activeCues, removeCue(), and removeTextTrack() support. findTrackElement() also searches a host's open shadow root so wrapped native media can resolve their <track> elements.

Usage

Create a programmatic track in React:

function MetadataTrack() {
  const metadata = useCreateTextTrack({ kind: 'metadata', label: 'ad-cues' });

  useEffect(() => {
    if (!metadata) return;

    const cue = new VTTCue(10, 20, 'midroll');
    metadata.addCue(cue);

    return () => metadata.removeCue(cue);
  }, [metadata]);

  return null;
}

Observe a track and its current cues:

const captionKinds = ['captions', 'subtitles'] as const;

function CurrentCaption() {
  const track = useActiveTextTrack(captionKinds);
  const cues = useActiveTextCues(track);

  return <output>{cues.map((cue) => cue.text).filter(Boolean).join('\n')}</output>;
}

Use the HTML controller inside a reactive custom element:

class ChapterTitleElement extends UIElement {
  readonly #track = new TextTrackController(this, 'chapters');

  protected override update() {
    this.textContent = this.#track.activeCues[0]?.text ?? '';
  }
}

URL-backed WebVTT remains native in both frameworks:

<Video>
  <track kind="captions" src="/captions/en.vtt" srcLang="en" label="English" default />
</Video>

There is intentionally no uppercase <Track> component: native <track> already composes with every native-backed Video.js media component and retains browser loading, default selection, and fallback semantics. The implementation also keeps the React stores specialized and private rather than exporting a generic createExternalStore, and it does not add a runtime kind parser for a TypeScript-only API.

Documentation

  • add generated-reference-backed pages for useCreateTextTrack, useActiveTextTrack, useTextCues, useActiveTextCues, and TextTrackController
  • register each page in the framework-filtered Utilities sidebar
  • cross-link the existing Text tracks feature reference to the new direct track APIs

Testing

  • pnpm -F @videojs/utils test (483 tests)
  • pnpm -F @videojs/media test
  • pnpm -F @videojs/html test (423 tests)
  • pnpm -F @videojs/react test (512 tests)
  • pnpm -F site test scripts/api-docs-builder/src/tests/e2e.test.ts (162 tests)
  • pnpm -F site api-docs
  • pnpm -F site astro check (0 errors; existing repository hints only)
  • pnpm typecheck
  • pnpm lint
  • pnpm check:workspace
  • rendered all five new API pages and both framework variants of the Text tracks feature page locally

Review follow-up

  • Rename useTextTrack to useCreateTextTrack; it creates and owns a track, so the accessor-style name read wrong beside useActiveTextTrack.
  • Native TextTrack.addCue() fires no event, so useTextCues never saw programmatic cues. Cue writes now go through the track handle (addCue/removeCue) and notify every watchTextTrackCues observer.
  • Browsers discard cues added before a <track> element's load settles (Chromium wipes them on both load and error). The handle queues writes until the element reports either event, and applies a requested disabled mode only after that.
  • useActiveTextTrack keys its subscription by the joined kinds, so an inline kind array no longer resubscribes every render.
  • Add apps/e2e/tests/text-track-cues.spec.ts, which verifies in a real browser that a cue on a src-less programmatic track fires cuechange and that destroy() removes the element. Passes in Chromium and Firefox with tracing off; locally, trace: 'on' fails on any page that streams remote video, which is unrelated to this spec.
  • Rebased onto main.

@netlify

netlify Bot commented Aug 26, 2026

Copy link
Copy Markdown

Deploy Preview for vjs10-site ready!

Name Link
🔨 Latest commit ec2a435
🔍 Latest deploy log https://app.netlify.com/projects/vjs10-site/deploys/6a988fde2eb4520008e6971e
😎 Deploy Preview https://deploy-preview-2439--vjs10-site.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.
🤖 Make changes Run an agent on this branch

To edit notification comments on pull requests, go to your Netlify project configuration.

@vercel

vercel Bot commented Aug 26, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated
v10-sandbox Ready Ready Preview Sep 2, 2026 9:07pm UTC

Request Review

@pkg-pr-new

pkg-pr-new Bot commented Aug 26, 2026

Copy link
Copy Markdown
@videojs/core

pnpm add https://pkg.pr.new/@videojs/core@2439

@videojs/element

pnpm add https://pkg.pr.new/@videojs/element@2439

@videojs/html

pnpm add https://pkg.pr.new/@videojs/html@2439

@videojs/media

pnpm add https://pkg.pr.new/@videojs/media@2439

@videojs/react

pnpm add https://pkg.pr.new/@videojs/react@2439

@videojs/spf

pnpm add https://pkg.pr.new/@videojs/spf@2439

@videojs/store

pnpm add https://pkg.pr.new/@videojs/store@2439

@videojs/utils

pnpm add https://pkg.pr.new/@videojs/utils@2439

commit: ec2a435

@github-actions

github-actions Bot commented Aug 26, 2026

Copy link
Copy Markdown
Contributor

📦 Bundle Size Report

🎨 @videojs/html

Path Base initial PR initial Initial diff Lazy diff
/media/dash-video 211.61 kB 211.92 kB +319 B (+0.1%)
Small changes (62, ≤ 300 B)
Path Base initial PR initial Initial diff Lazy diff
/video/minimal-skin 60.28 kB 60.30 kB +18 B (+0.0%) 0 B
/video/player 9.70 kB 9.70 kB -1 B (-0.0%) 0 B
/video/skin 60.54 kB 60.45 kB -88 B (-0.1%) 0 B
/audio/minimal-skin 46.43 kB 46.40 kB -35 B (-0.1%) 0 B
/audio/skin 50.32 kB 50.31 kB -3 B (-0.0%) 0 B
/background/skin 9.77 kB 9.78 kB +13 B (+0.1%) 0 B
/live-video/minimal-skin 54.40 kB 54.40 kB +2 B (+0.0%) 0 B
/live-video/player 9.06 kB 9.07 kB +11 B (+0.1%) 0 B
/live-video/skin 56.99 kB 57.00 kB +17 B (+0.0%) 0 B
/live-audio/minimal-skin 39.72 kB 39.70 kB -15 B (-0.0%) 0 B
/live-audio/skin 43.33 kB 43.34 kB +2 B (+0.0%) 0 B
/ui/airplay-button 11.26 kB 11.28 kB +14 B (+0.1%) 0 B
/ui/audio-track-radio-group 11.34 kB 11.37 kB +21 B (+0.2%) 0 B
/ui/buffering-indicator 8.61 kB 8.62 kB +16 B (+0.2%) 0 B
/ui/captions-button 11.51 kB 11.53 kB +17 B (+0.1%) 0 B
/ui/captions-radio-group 11.43 kB 11.45 kB +16 B (+0.1%) 0 B
/ui/cast-button 11.25 kB 11.26 kB +12 B (+0.1%) 0 B
/ui/container 9.47 kB 9.48 kB +6 B (+0.1%) 0 B
/ui/controls 8.81 kB 8.84 kB +28 B (+0.3%) 0 B
/ui/error-dialog 13.01 kB 13.02 kB +17 B (+0.1%) 0 B
/ui/gesture 9.56 kB 9.57 kB +8 B (+0.1%) 0 B
/ui/hotkey 9.97 kB 9.97 kB +5 B (+0.0%) 0 B
/ui/live-button 9.74 kB 9.75 kB +11 B (+0.1%) 0 B
/ui/menu 20.06 kB 20.07 kB +7 B (+0.0%) 0 B
/ui/mute-button 11.28 kB 11.27 kB -7 B (-0.1%) 0 B
/ui/pip-button 11.22 kB 11.24 kB +16 B (+0.1%) 0 B
/ui/play-button 11.23 kB 11.24 kB +9 B (+0.1%) 0 B
/ui/playback-rate-button 11.32 kB 11.33 kB +7 B (+0.1%) 0 B
/ui/playback-rate-radio-group 11.34 kB 11.36 kB +13 B (+0.1%) 0 B
/ui/poster 8.67 kB 8.68 kB +16 B (+0.2%) 0 B
/ui/quality-radio-group 11.94 kB 11.95 kB +12 B (+0.1%) 0 B
/ui/seek-button 11.27 kB 11.26 kB -15 B (-0.1%) 0 B
/ui/seek-indicator 12.69 kB 12.71 kB +13 B (+0.1%) 0 B
/ui/slider 12.12 kB 12.13 kB +7 B (+0.1%) 0 B
/ui/status-announcer 10.59 kB 10.59 kB +9 B (+0.1%) 0 B
/ui/status-indicator 13.03 kB 13.03 kB -1 B (-0.0%) 0 B
/ui/thumbnail 9.84 kB 9.85 kB +11 B (+0.1%) 0 B
/ui/time 10.87 kB 10.90 kB +26 B (+0.2%) 0 B
/ui/time-slider 15.04 kB 15.08 kB +34 B (+0.2%) 0 B
/ui/time-slider-chapter-title 8.79 kB 8.79 kB +7 B (+0.1%) 0 B
/ui/time-slider-chapters 9.76 kB 9.76 kB +8 B (+0.1%) 0 B
/ui/title 8.17 kB 8.18 kB +14 B (+0.2%) 0 B
/ui/volume-indicator 13.03 kB 13.05 kB +16 B (+0.1%) 0 B
/ui/volume-popover 14.72 kB 14.74 kB +16 B (+0.1%) 0 B
/ui/volume-slider 12.79 kB 12.77 kB -20 B (-0.2%) 0 B
/extensions/google-cast 6.52 kB 6.69 kB +174 B (+2.6%)
/extensions/mux-data 26.94 kB 27.16 kB +223 B (+0.8%)
/media/mux-video 142.40 kB 142.51 kB +112 B (+0.1%)
/media/mux-audio 142.23 kB 142.41 kB +175 B (+0.1%)
/media/cloudflare-video 6.28 kB 6.28 kB +2 B (+0.0%)
/media/hls-audio 22.09 kB 22.25 kB +166 B (+0.7%)
/media/hls-video 28.04 kB 28.18 kB +143 B (+0.5%)
/media/hlsjs-video 141.22 kB 141.29 kB +66 B (+0.0%)
/media/native-hls-video 7.57 kB 7.75 kB +177 B (+2.3%)
/media/shaka-video 199.18 kB 199.29 kB +112 B (+0.1%)
/video (default) 62.05 kB 62.02 kB -29 B (-0.0%) 0 B
/video (default + hls) 200.93 kB 201.08 kB +150 B (+0.1%) 0 B
/video (minimal) 61.78 kB 61.78 kB -2 B (-0.0%) 0 B
/video (minimal + hls) 200.76 kB 200.88 kB +118 B (+0.1%) 0 B
/audio (default) 51.81 kB 51.77 kB -40 B (-0.1%) 0 B
/audio (minimal) 47.89 kB 47.87 kB -14 B (-0.0%) 0 B
/background 11.52 kB 11.53 kB +4 B (+0.0%) 0 B
Presets (7)
Entry Initial Lazy
/video (default) 62.02 kB 56.87 kB
/video (default + hls) 201.08 kB 56.87 kB
/video (minimal) 61.78 kB 56.87 kB
/video (minimal + hls) 200.88 kB 56.87 kB
/audio (default) 51.77 kB 56.87 kB
/audio (minimal) 47.87 kB 56.87 kB
/background 11.53 kB 56.87 kB
Media (18)
Entry Initial
/media/mux-video 142.51 kB
/media/mux-audio 142.41 kB
/media/background-video 1.16 kB
/media/cloudflare-video 6.28 kB
/media/dash-video 211.92 kB
/media/hls-audio 22.25 kB
/media/hls-background-video 16.80 kB
/media/hls-video 28.18 kB
/media/hlsjs-video 141.29 kB
/media/mux-background-video 16.83 kB
/media/native-hls-video 7.75 kB
/media/shaka-video 199.29 kB
/media/spotify-audio 6.27 kB
/media/tiktok-video 6.33 kB
/media/twitch-video 6.22 kB
/media/vimeo-video 13.05 kB
/media/wistia-video 60.23 kB
/media/youtube-video 6.81 kB
Extensions (2)
Entry Initial
/extensions/google-cast 6.69 kB
/extensions/mux-data 27.16 kB
Players (5)
Entry Initial Lazy
/video/player 9.70 kB 56.87 kB
/audio/player 6.14 kB 56.87 kB
/background/player 4.46 kB 56.87 kB
/live-video/player 9.07 kB 56.87 kB
/live-audio/player 6.15 kB 56.87 kB
Skins (29)
Entry Type Initial Lazy
/video/minimal-skin.css css 6.63 kB
/video/skin.css css 6.45 kB
/video/minimal-skin js 60.30 kB 56.87 kB
/video/minimal-skin2 js 7.19 kB
/video/skin js 60.45 kB 56.87 kB
/video/skin2 js 7.04 kB
/audio/minimal-skin.css css 4.95 kB
/audio/skin.css css 4.88 kB
/audio/minimal-skin js 46.40 kB 56.87 kB
/audio/minimal-skin2 js 5.36 kB
/audio/skin js 50.31 kB 56.87 kB
/audio/skin2 js 5.32 kB
/background/skin.css css 145 B
/background/skin js 9.78 kB 56.87 kB
/background/skin2 js 190 B
/live-video/minimal-skin.css css 6.63 kB
/live-video/skin.css css 6.45 kB
/live-video/minimal-skin js 54.40 kB 56.87 kB
/live-video/minimal-skin2 js 7.19 kB
/live-video/skin js 57.00 kB 56.87 kB
/live-video/skin2 js 7.04 kB
/live-audio/minimal-skin.css css 4.95 kB
/live-audio/skin.css css 4.88 kB
/live-audio/minimal-skin js 39.70 kB 56.87 kB
/live-audio/minimal-skin2 js 5.36 kB
/live-audio/skin js 43.34 kB 56.87 kB
/live-audio/skin2 js 5.32 kB
/global.css css 190 B
/shared.css css 118 B
UI Components (50)
Entry Initial Lazy
/ui/airplay-button 11.28 kB 56.87 kB
/ui/alert-dialog 9.44 kB 56.87 kB
/ui/audio-track-radio-group 11.37 kB 56.87 kB
/ui/buffering-indicator 8.62 kB 56.87 kB
/ui/captions-button 11.53 kB 56.87 kB
/ui/captions-radio-group 11.45 kB 56.87 kB
/ui/cast-button 11.26 kB 56.87 kB
/ui/container 9.48 kB 56.87 kB
/ui/controls 8.84 kB 56.87 kB
/ui/controls-backdrop 1.69 kB 56.87 kB
/ui/controls-content 1.67 kB 56.87 kB
/ui/dialog 9.35 kB 56.87 kB
/ui/dialog-backdrop 1.70 kB 56.87 kB
/ui/dialog-close 2.06 kB 56.87 kB
/ui/dialog-description 1.69 kB 56.87 kB
/ui/dialog-popup 1.90 kB 56.87 kB
/ui/dialog-title 1.70 kB 56.87 kB
/ui/error-dialog 13.02 kB 56.87 kB
/ui/fullscreen-button 11.27 kB 56.87 kB
/ui/gesture 9.57 kB 56.87 kB
/ui/hotkey 9.97 kB 56.87 kB
/ui/live-button 9.75 kB 56.87 kB
/ui/menu 20.07 kB 56.87 kB
/ui/mute-button 11.27 kB 56.87 kB
/ui/pip-button 11.24 kB 56.87 kB
/ui/play-button 11.24 kB 56.87 kB
/ui/playback-rate-button 11.33 kB 56.87 kB
/ui/playback-rate-radio-group 11.36 kB 56.87 kB
/ui/popover 8.56 kB 56.87 kB
/ui/poster 8.68 kB 56.87 kB
/ui/quality-radio-group 11.95 kB 56.87 kB
/ui/seek-button 11.26 kB 56.87 kB
/ui/seek-indicator 12.71 kB 56.87 kB
/ui/seek-indicator-value 1.10 kB
/ui/slider 12.13 kB 56.87 kB
/ui/status-announcer 10.59 kB 56.87 kB
/ui/status-indicator 13.03 kB 56.87 kB
/ui/status-indicator-value 1.10 kB
/ui/thumbnail 9.85 kB 56.87 kB
/ui/time 10.90 kB 56.87 kB
/ui/time-slider 15.08 kB 56.87 kB
/ui/time-slider-chapter-title 8.79 kB 56.87 kB
/ui/time-slider-chapters 9.76 kB 56.87 kB
/ui/title 8.18 kB 56.87 kB
/ui/tooltip 9.95 kB 56.87 kB
/ui/volume-indicator 13.05 kB 56.87 kB
/ui/volume-indicator-fill 1.11 kB
/ui/volume-indicator-value 1.10 kB
/ui/volume-popover 14.74 kB 56.87 kB
/ui/volume-slider 12.77 kB 56.87 kB
⚛️ @videojs/react — 68 small size changes
Path Base initial PR initial Initial diff Lazy diff
/extensions/google-cast 5.34 kB 5.50 kB +169 B (+3.1%)
/extensions/mux-data 25.70 kB 25.90 kB +209 B (+0.8%)
/media/mux-video/hls-js 140.61 kB 140.76 kB +154 B (+0.1%)
/media/mux-video/spf 27.39 kB 27.55 kB +161 B (+0.6%)
/media/mux-audio/hls-js 140.55 kB 140.59 kB +37 B (+0.0%)
/media/mux-audio/spf 21.32 kB 21.45 kB +135 B (+0.6%)
/media/dash-video 210.29 kB 210.21 kB -81 B (-0.0%)
/media/hls-audio 20.33 kB 20.53 kB +200 B (+1.0%)
/media/hls-video 26.34 kB 26.46 kB +124 B (+0.5%)
/media/hlsjs-video 139.44 kB 139.73 kB +295 B (+0.2%)
/media/mux-audio 140.56 kB 140.51 kB -50 B (-0.0%)
/media/mux-video 140.54 kB 140.75 kB +222 B (+0.2%)
/media/native-hls-video 5.76 kB 5.94 kB +181 B (+3.1%)
/media/shaka-video 197.52 kB 197.64 kB +119 B (+0.1%)
/video/minimal-skin 48.18 kB 48.18 kB -4 B (-0.0%) 0 B
/video/minimal-skin.tailwind 55.09 kB 55.12 kB +31 B (+0.1%) 0 B
/video/player 7.56 kB 7.56 kB +6 B (+0.1%) 0 B
/video/skin 48.16 kB 48.17 kB +12 B (+0.0%) 0 B
/video/skin.tailwind 54.92 kB 54.91 kB -2 B (-0.0%) 0 B
/audio/minimal-skin 37.54 kB 37.50 kB -45 B (-0.1%) 0 B
/audio/minimal-skin.tailwind 42.61 kB 42.63 kB +27 B (+0.1%) 0 B
/audio/skin 37.43 kB 37.43 kB -2 B (-0.0%) 0 B
/audio/skin.tailwind 42.56 kB 42.60 kB +45 B (+0.1%) 0 B
/live-video/minimal-skin 40.35 kB 40.36 kB +11 B (+0.0%) 0 B
/live-video/minimal-skin.tailwind 47.09 kB 47.07 kB -28 B (-0.1%) 0 B
/live-video/player 6.96 kB 6.96 kB +4 B (+0.1%) 0 B
/live-video/skin 40.39 kB 40.38 kB -12 B (-0.0%) 0 B
/live-video/skin.tailwind 47.03 kB 47.00 kB -31 B (-0.1%) 0 B
/live-audio/minimal-skin 29.29 kB 29.30 kB +12 B (+0.0%) 0 B
/live-audio/minimal-skin.tailwind 33.28 kB 33.25 kB -30 B (-0.1%) 0 B
/live-audio/skin 29.30 kB 29.31 kB +19 B (+0.1%) 0 B
/live-audio/skin.tailwind 33.37 kB 33.40 kB +33 B (+0.1%) 0 B
/ui/airplay-button 10.41 kB 10.41 kB -3 B (-0.0%) 0 B
/ui/audio-track 7.93 kB 7.93 kB +4 B (+0.0%) 0 B
/ui/audio-track-radio-group 9.10 kB 9.11 kB +10 B (+0.1%) 0 B
/ui/buffering-indicator 7.77 kB 7.76 kB -11 B (-0.1%) 0 B
/ui/captions-button 10.39 kB 10.39 kB +4 B (+0.0%) 0 B
/ui/captions-radio-group 9.21 kB 9.21 kB -1 B (-0.0%) 0 B
/ui/cast-button 10.39 kB 10.39 kB +5 B (+0.0%) 0 B
/ui/controls 7.55 kB 7.56 kB +4 B (+0.1%) 0 B
/ui/error-dialog 11.77 kB 11.78 kB +11 B (+0.1%) 0 B
/ui/gesture 7.99 kB 7.99 kB +4 B (+0.0%) 0 B
/ui/hotkey 8.38 kB 8.38 kB +5 B (+0.1%) 0 B
/ui/live-button 8.76 kB 8.76 kB +6 B (+0.1%) 0 B
/ui/menu 18.90 kB 18.90 kB +5 B (+0.0%) 0 B
/ui/pip-button 10.35 kB 10.36 kB +6 B (+0.1%) 0 B
/ui/play-button 10.35 kB 10.34 kB -9 B (-0.1%) 0 B
/ui/playback-rate 7.99 kB 7.98 kB -5 B (-0.1%) 0 B
/ui/playback-rate-button 10.34 kB 10.35 kB +5 B (+0.0%) 0 B
/ui/playback-rate-radio-group 9.14 kB 9.15 kB +10 B (+0.1%) 0 B
/ui/poster 7.54 kB 7.54 kB +4 B (+0.1%) 0 B
/ui/quality-radio-group 9.59 kB 9.59 kB +5 B (+0.1%) 0 B
/ui/seek-indicator 11.95 kB 11.95 kB +2 B (+0.0%) 0 B
/ui/slider 12.73 kB 12.71 kB -16 B (-0.1%) 0 B
/ui/status-announcer 10.06 kB 10.06 kB +6 B (+0.1%) 0 B
/ui/status-indicator 12.26 kB 12.26 kB +6 B (+0.0%) 0 B
/ui/thumbnail 8.71 kB 8.71 kB +7 B (+0.1%) 0 B
/ui/time 9.74 kB 9.74 kB +1 B (+0.0%) 0 B
/ui/time-slider 14.15 kB 14.15 kB +2 B (+0.0%) 0 B
/ui/volume-indicator 12.32 kB 12.32 kB +4 B (+0.0%) 0 B
/ui/volume-popover 14.17 kB 14.18 kB +8 B (+0.1%) 0 B
/ui/volume-slider 12.11 kB 12.12 kB +12 B (+0.1%) 0 B
/video (default) 48.32 kB 48.28 kB -46 B (-0.1%) 0 B
/video (default + hls) 186.33 kB 186.45 kB +120 B (+0.1%) 0 B
/video (minimal) 48.32 kB 48.29 kB -36 B (-0.1%) 0 B
/video (minimal + hls) 186.25 kB 186.28 kB +34 B (+0.0%) 0 B
/audio (default) 37.53 kB 37.48 kB -52 B (-0.1%) 0 B
/audio (minimal) 37.55 kB 37.59 kB +33 B (+0.1%) 0 B
Presets (7)
Entry Initial Lazy
/video (default) 48.28 kB 56.87 kB
/video (default + hls) 186.45 kB 56.87 kB
/video (minimal) 48.29 kB 56.87 kB
/video (minimal + hls) 186.28 kB 56.87 kB
/audio (default) 37.48 kB 56.87 kB
/audio (minimal) 37.59 kB 56.87 kB
/background 581 B 56.87 kB
Media (22)
Entry Initial
/media/mux-video/hls-js 140.76 kB
/media/mux-video/spf 27.55 kB
/media/mux-audio/hls-js 140.59 kB
/media/mux-audio/spf 21.45 kB
/media/background-video 402 B
/media/cloudflare-video 4.34 kB
/media/dash-video 210.21 kB
/media/hls-audio 20.53 kB
/media/hls-background-video 16.46 kB
/media/hls-video 26.46 kB
/media/hlsjs-video 139.73 kB
/media/mux-audio 140.51 kB
/media/mux-background-video 16.45 kB
/media/mux-video 140.75 kB
/media/native-hls-video 5.94 kB
/media/shaka-video 197.64 kB
/media/spotify-audio 4.22 kB
/media/tiktok-video 4.18 kB
/media/twitch-video 4.30 kB
/media/vimeo-video 11.19 kB
/media/wistia-video 60.05 kB
/media/youtube-video 4.86 kB
Extensions (2)
Entry Initial
/extensions/google-cast 5.50 kB
/extensions/mux-data 25.90 kB
Players (5)
Entry Initial Lazy
/video/player 7.56 kB 56.87 kB
/audio/player 4.00 kB 56.87 kB
/background/player 2.27 kB 56.87 kB
/live-video/player 6.96 kB 56.87 kB
/live-audio/player 3.99 kB 56.87 kB
Skins (26)
Entry Type Initial Lazy
/video/minimal-skin.css css 6.52 kB
/video/skin.css css 6.35 kB
/video/minimal-skin js 48.18 kB 56.87 kB
/video/minimal-skin.tailwind js 55.12 kB 56.87 kB
/video/skin js 48.17 kB 56.87 kB
/video/skin.tailwind js 54.91 kB 56.87 kB
/audio/minimal-skin.css css 4.81 kB
/audio/skin.css css 4.74 kB
/audio/minimal-skin js 37.50 kB 56.87 kB
/audio/minimal-skin.tailwind js 42.63 kB 56.87 kB
/audio/skin js 37.43 kB 56.87 kB
/audio/skin.tailwind js 42.60 kB 56.87 kB
/background/skin.css css 90 B
/background/skin js 277 B
/live-video/minimal-skin.css css 6.52 kB
/live-video/skin.css css 6.35 kB
/live-video/minimal-skin js 40.36 kB 56.87 kB
/live-video/minimal-skin.tailwind js 47.07 kB 56.87 kB
/live-video/skin js 40.38 kB 56.87 kB
/live-video/skin.tailwind js 47.00 kB 56.87 kB
/live-audio/minimal-skin.css css 4.81 kB
/live-audio/skin.css css 4.74 kB
/live-audio/minimal-skin js 29.30 kB 56.87 kB
/live-audio/minimal-skin.tailwind js 33.25 kB 56.87 kB
/live-audio/skin js 29.31 kB 56.87 kB
/live-audio/skin.tailwind js 33.40 kB 56.87 kB
UI Components (39)
Entry Initial Lazy
/ui/airplay-button 10.41 kB 56.87 kB
/ui/alert-dialog 4.71 kB 56.87 kB
/ui/audio-track 7.93 kB 56.87 kB
/ui/audio-track-radio-group 9.11 kB 56.87 kB
/ui/buffering-indicator 7.76 kB 56.87 kB
/ui/captions-button 10.39 kB 56.87 kB
/ui/captions-radio-group 9.21 kB 56.87 kB
/ui/cast-button 10.39 kB 56.87 kB
/ui/controls 7.56 kB 56.87 kB
/ui/dialog 4.73 kB 56.87 kB
/ui/error-dialog 11.78 kB 56.87 kB
/ui/fullscreen-button 10.36 kB 56.87 kB
/ui/gesture 7.99 kB 56.87 kB
/ui/hotkey 8.38 kB 56.87 kB
/ui/live-button 8.76 kB 56.87 kB
/ui/menu 18.90 kB 56.87 kB
/ui/mute-button 10.40 kB 56.87 kB
/ui/pip-button 10.36 kB 56.87 kB
/ui/play-button 10.34 kB 56.87 kB
/ui/playback-rate 7.98 kB 56.87 kB
/ui/playback-rate-button 10.35 kB 56.87 kB
/ui/playback-rate-radio-group 9.15 kB 56.87 kB
/ui/popover 8.13 kB 56.87 kB
/ui/poster 7.54 kB 56.87 kB
/ui/quality 8.42 kB 56.87 kB
/ui/quality-radio-group 9.59 kB 56.87 kB
/ui/seek-button 10.37 kB 56.87 kB
/ui/seek-indicator 11.95 kB 56.87 kB
/ui/slider 12.71 kB 56.87 kB
/ui/status-announcer 10.06 kB 56.87 kB
/ui/status-indicator 12.26 kB 56.87 kB
/ui/thumbnail 8.71 kB 56.87 kB
/ui/time 9.74 kB 56.87 kB
/ui/time-slider 14.15 kB 56.87 kB
/ui/title 7.24 kB 56.87 kB
/ui/tooltip 8.90 kB 56.87 kB
/ui/volume-indicator 12.32 kB 56.87 kB
/ui/volume-popover 14.18 kB 56.87 kB
/ui/volume-slider 12.12 kB 56.87 kB
🧩 @videojs/core — 2 small size changes
Path Base initial PR initial Initial diff Lazy diff
. 13.02 kB 13.07 kB +52 B (+0.4%)
/dom 22.42 kB 22.45 kB +30 B (+0.1%) 0 B
Entries (76)
Entry Initial Lazy
. 13.07 kB
/dom 22.45 kB 56.87 kB
/vjsc 1.74 kB
/i18n 3.23 kB 56.87 kB
/i18n/locales/all 34.97 kB
/i18n/locales/ar 1.22 kB
/i18n/locales/az 1.09 kB
/i18n/locales/bg 1.29 kB
/i18n/locales/bn 1.23 kB
/i18n/locales/bs 1.00 kB
/i18n/locales/ca 1.07 kB
/i18n/locales/cs 1.07 kB
/i18n/locales/cy 1.02 kB
/i18n/locales/da 1006 B
/i18n/locales/de 1.09 kB
/i18n/locales/el 1.49 kB
/i18n/locales/en 785 B
/i18n/locales/es 1018 B
/i18n/locales/et 1.05 kB
/i18n/locales/eu 1.03 kB
/i18n/locales/fa 1.22 kB
/i18n/locales/fi 1.04 kB
/i18n/locales/fr 1.07 kB
/i18n/locales/gd 1.10 kB
/i18n/locales/gl 1022 B
/i18n/locales/he 1.14 kB
/i18n/locales/hi 1.30 kB
/i18n/locales/hr 1.02 kB
/i18n/locales/hu 1.10 kB
/i18n/locales/id 919 B
/i18n/locales/it 1.01 kB
/i18n/locales/ja 1.18 kB
/i18n/locales/ko 1.14 kB
/i18n/locales/lt 1.02 kB
/i18n/locales/lv 1.09 kB
/i18n/locales/mr 1.30 kB
/i18n/locales/nb 987 B
/i18n/locales/ne 1.29 kB
/i18n/locales/nl 1.00 kB
/i18n/locales/nn 987 B
/i18n/locales/oc 1.08 kB
/i18n/locales/pl 1.14 kB
/i18n/locales/pt 1.03 kB
/i18n/locales/pt-BR 1.03 kB
/i18n/locales/pt-PT 1.01 kB
/i18n/locales/ro 1.07 kB
/i18n/locales/ru 1.40 kB
/i18n/locales/sk 1.10 kB
/i18n/locales/sl 1.04 kB
/i18n/locales/sr 1.07 kB
/i18n/locales/sv 1.01 kB
/i18n/locales/te 1.32 kB
/i18n/locales/th 1.29 kB
/i18n/locales/tr 1.08 kB
/i18n/locales/uk 1.41 kB
/i18n/locales/vi 1.11 kB
/i18n/locales/zh 1.02 kB
/i18n/locales/zh-CN 1.02 kB
/i18n/locales/zh-TW 1.02 kB
/i18n/text/airplay 101 B
/i18n/text/buttons 137 B
/i18n/text/captions 93 B
/i18n/text/cast 114 B
/i18n/text/common 90 B
/i18n/text/container 75 B
/i18n/text/errors 285 B
/i18n/text/fullscreen 98 B
/i18n/text/live 126 B
/i18n/text/menu 249 B
/i18n/text/pip 101 B
/i18n/text/playback 80 B
/i18n/text/seek 105 B
/i18n/text/slider 65 B
/i18n/text/status 222 B
/i18n/text/time 280 B
/i18n/text/volume 133 B
🏷️ @videojs/element — no changes
Entries (2)
Entry Initial
. 1.05 kB
/context 943 B
📦 @videojs/store — no changes
Entries (3)
Entry Initial
. 1.72 kB
/html 703 B
/react 365 B
🔧 @videojs/utils — 1 small size change
Path Base initial PR initial Initial diff Lazy diff
/dom 5.02 kB 5.04 kB +21 B (+0.4%)
Entries (13)
Entry Initial
/array 271 B
/dom 5.04 kB
/events 320 B
/function 386 B
/i18n 249 B
/jwt 176 B
/object 535 B
/predicate 297 B
/percent 281 B
/string 239 B
/style 212 B
/time 976 B
/number 199 B

📦 @videojs/media

Path Base initial PR initial Initial diff Lazy diff
/dom 121 B 1.07 kB +977 B (+807.4%)
/dom/dash 209.83 kB 3.46 kB -206.36 kB (-98.3%)
/dom/wistia 60.09 kB 1.89 kB -58.20 kB (-96.9%)
/dom/hls-js 139.16 kB 139.45 kB +303 B (+0.2%)
/dom/mux 162.88 kB 163.19 kB +320 B (+0.2%)
Small changes (11, ≤ 300 B)
Path Base initial PR initial Initial diff Lazy diff
/dom/audio-host 1.10 kB 1.29 kB +199 B (+17.7%)
/dom/dash 209.83 kB 209.79 kB -39 B (-0.0%)
/dom/media-host 1.20 kB 1.39 kB +187 B (+15.2%)
/dom/native-hls 5.25 kB 5.45 kB +203 B (+3.8%)
/dom/shaka 197.02 kB 197.28 kB +266 B (+0.1%)
/dom/spotify 3.58 kB 3.58 kB +1 B (+0.0%)
/dom/tiktok 3.57 kB 3.56 kB -9 B (-0.2%)
/dom/twitch 3.62 kB 3.62 kB -2 B (-0.1%)
/dom/video-host 1.39 kB 1.59 kB +204 B (+14.4%)
/dom/vimeo 10.62 kB 10.59 kB -27 B (-0.2%)
/dom/youtube 4.25 kB 4.25 kB -1 B (-0.0%)
Entries (23)
Entry Initial
. 1.07 kB
/dom 1.07 kB
/dom/dash 3.46 kB
/dom/wistia 1.89 kB
/dom/audio-host 1.29 kB
/dom/cloudflare 3.69 kB
/dom/custom-media-element 2.13 kB
/dom/dash 209.79 kB
/dom/google-cast 4.06 kB
/dom/hls-js 139.45 kB
/dom/media-host 1.39 kB
/dom/media-played-ranges 576 B
/dom/mux 163.19 kB
/dom/native-hls 5.45 kB
/dom/shaka 197.28 kB
/dom/spotify 3.58 kB
/dom/tiktok 3.56 kB
/dom/twitch 3.62 kB
/dom/video-host 1.59 kB
/dom/vimeo 10.59 kB
/dom/wistia 60.09 kB
/dom/youtube 4.25 kB
/media-tracks 2.05 kB
📦 @videojs/spf — 5 small size changes
Path Base initial PR initial Initial diff Lazy diff
/hls 22.06 kB 22.11 kB +50 B (+0.2%)
/hls-audio 19.82 kB 20.00 kB +187 B (+0.9%)
/hls-video 25.85 kB 25.99 kB +140 B (+0.5%)
/mux-audio 20.76 kB 21.00 kB +250 B (+1.2%)
/mux-video 26.67 kB 26.86 kB +200 B (+0.7%)
Entries (10)
Entry Initial
. 4.55 kB
/dom 6.60 kB
/hls 22.11 kB
/media-tracks 504 B
/hls-background-video 15.89 kB
/hls-audio 20.00 kB
/hls-video 25.99 kB
/mux-audio 21.00 kB
/mux-background-video 15.89 kB
/mux-video 26.86 kB

ℹ️ How to interpret

Each 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 pnpm size locally to check current initial sizes.

Rename the React creation hook to useCreateTextTrack, notify cue observers when cues are written through a track handle, and defer those writes until the backing <track> element settles because browsers discard cues added before its load completes. Key useActiveTextTrack subscriptions by kind so inline arrays do not resubscribe, and add a browser e2e for src-less programmatic tracks.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant