Skip to content

perf(VideoPlayer): lazy-load react-player to keep it out of the main bundle - #3204

Merged
oliverlaz merged 2 commits into
masterfrom
perf/lazy-video-player
Jun 3, 2026
Merged

perf(VideoPlayer): lazy-load react-player to keep it out of the main bundle#3204
oliverlaz merged 2 commits into
masterfrom
perf/lazy-video-player

Conversation

@oliverlaz

@oliverlaz oliverlaz commented Jun 3, 2026

Copy link
Copy Markdown
Member

🎯 Goal

Stop pulling react-player (~2 MB) into the SDK's eager import graph β€” finding #4 from the Vercel React best-practices audit (PERFORMANCE_AUDIT.md, bundle-dynamic-imports). react-player is only needed when a default video attachment renders, yet the static import chain (index β†’ Attachment β†’ VideoAttachment β†’ VideoPlayer) forced it into every consumer's main chunk β€” including for consumers who never show video or who override the player.

πŸ›  Implementation details

  • Moved the react-player render (plus the existing .default CJS-interop unwrap) into a new default-exported internal module VideoPlayer/ReactPlayerWrapper.tsx.
  • VideoPlayer.tsx now loads it via React.lazy(() => import('./ReactPlayerWrapper')) inside <React.Suspense>. The ComponentContext.VideoPlayer override branch and the public VideoPlayer export / VideoPlayerProps are unchanged.
  • Suspense fallback: the overridable LoadingIndicator (read from ComponentContext), wrapped in .str-chat__video-player-loading to fill + center within the player box so it doesn't flash as a stray top-left icon or shift layout when the player mounts. New SCSS VideoPlayer/styling/VideoPlayer.scss, @used from the VideoPlayer styling entry.
  • Build proof: in dist/es/index.mjs there is no static from "react-player" β€” instead import("./ReactPlayerWrapper.<hash>.mjs"), and react-player is imported only inside that wrapper chunk. The consumer's bundler thus emits it as a separate, on-demand chunk; consumers overriding VideoPlayer/Media never load it.
  • Accepted trade-off: React.lazy isn't server-renderable on React 17; default video rendering is client-only. SSR consumers can supply a synchronous VideoPlayer via ComponentContext (unchanged path).
  • Tests: existing VideoAttachment / Attachment / Gallery suites render via overrides and are unaffected. Added VideoPlayer/__tests__/VideoPlayer.test.tsx covering the lazy default render (async) and the override path (no react-player load).

Verification: yarn types, ESLint (--max-warnings 0), Prettier (TS + SCSS), yarn build-styling (rule present in dist/css/index.css), vite build chunk-split confirmed, and the video/attachment/gallery Vitest suites all pass.

🎨 UI Changes

No change to the rendered player. One new transient state: a centered loading spinner shown for the brief moment the react-player chunk is fetched (previously the player simply appeared once its eagerly-bundled code ran). No screenshots needed.


πŸ“¦ Note β€” second commit vendors agent skills (separate concern)

This PR also contains a separate chore: commit, chore: vendor Vercel agent skills used for the performance audit, unrelated to the VideoPlayer change. It vendors the three vercel-labs/agent-skills used to produce the audit (vercel-composition-patterns, vercel-react-best-practices, web-design-guidelines) under .agents/skills, symlinked from .claude/skills (relative, in-repo symlinks) and pinned via skills-lock.json. The third-party content is added to .prettierignore rather than reformatted to repo style.

It's isolated in its own commit, so it can be split into a dedicated PR or dropped if reviewers would prefer to keep this PR perf-only.

Summary by CodeRabbit

  • New Features

    • Added loading indicator for the video player component while it initializes.
  • Improvements

    • Enhanced video player performance through optimized code loading and component architecture.

…bundle

The default VideoPlayer statically imported react-player (~2 MB), pulling it
into the SDK's eager import graph via index -> Attachment -> VideoAttachment.
It is now code-split behind React.lazy: the react-player render moves to a
default-exported ReactPlayerWrapper, dynamically imported so the consumer's
bundler emits it as a separate chunk fetched only when a default video player
renders. Consumers who override VideoPlayer via ComponentContext never load it.

While the chunk loads, a Suspense fallback shows the overridable LoadingIndicator
centered to fill the player box.
@github-actions

github-actions Bot commented Jun 3, 2026

Copy link
Copy Markdown

Size Change: +1.04 kB (+0.16%)

Total Size: 653 kB

πŸ“¦ View Changed
Filename Size Change
dist/cjs/index.js 256 kB -10 B (0%)
dist/cjs/ReactPlayerWrapper.js 545 B +545 B (new file) πŸ†•
dist/css/index.css 39.7 kB +28 B (+0.07%)
dist/es/emojis.mjs 2.47 kB +1 B (+0.04%)
dist/es/index.mjs 254 kB -11 B (0%)
dist/es/ReactPlayerWrapper.mjs 485 B +485 B (new file) πŸ†•
ℹ️ View Unchanged
Filename Size
dist/cjs/audioProcessing.js 1.74 kB
dist/cjs/emojis.js 2.54 kB
dist/cjs/mp3-encoder.js 814 B
dist/cjs/useNotificationApi.js 46.6 kB
dist/css/emoji-picker.css 178 B
dist/css/emoji-replacement.css 456 B
dist/es/audioProcessing.mjs 1.65 kB
dist/es/mp3-encoder.mjs 768 B
dist/es/useNotificationApi.mjs 45.4 kB

compressed-size-action

@codecov

codecov Bot commented Jun 3, 2026

Copy link
Copy Markdown

Codecov Report

βœ… All modified and coverable lines are covered by tests.
βœ… Project coverage is 83.70%. Comparing base (8c018a4) to head (92c4593).
⚠️ Report is 1 commits behind head on master.

Additional details and impacted files
@@           Coverage Diff           @@
##           master    #3204   +/-   ##
=======================================
  Coverage   83.70%   83.70%           
=======================================
  Files         437      438    +1     
  Lines       13145    13148    +3     
  Branches     4255     4256    +1     
=======================================
+ Hits        11003    11006    +3     
  Misses       2142     2142           

β˜” View full report in Codecov by Sentry.
πŸ“’ Have feedback on the report? Share it here.

πŸš€ New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • πŸ“¦ JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Comment thread src/components/VideoPlayer/VideoPlayer.tsx
Adds the three vercel-labs/agent-skills used during the performance audit
(vercel-composition-patterns, vercel-react-best-practices, web-design-guidelines)
under .agents/skills, symlinked from .claude/skills and pinned via
skills-lock.json. The content is third-party, so it is added to .prettierignore
rather than reformatted to repo style.
@coderabbitai

coderabbitai Bot commented Jun 3, 2026

Copy link
Copy Markdown

Review Change Stack

Caution

Review failed

Pull request was closed or merged during review

πŸ“ Walkthrough

Walkthrough

This PR adds comprehensive React composition patterns and performance best practices documentation from Vercel Labs as vendored agent skills, plus refactors the VideoPlayer component for lazy loading with Suspense boundaries.

Changes

Agent Skills Documentation

Layer / File(s) Summary
Composition Patterns skill definition
.agents/skills/vercel-composition-patterns/SKILL.md, README.md, metadata.json, rules/_sections.md, rules/_template.md
Introduces the React composition patterns skill with metadata, structure, prioritized rule categories, quick reference, and markdown rule template.
Composition Patterns rules
.agents/skills/vercel-composition-patterns/rules/*.md
Seven rule documents covering avoiding boolean prop proliferation, compound components, state management decoupling, generic context interfaces, lifting state, explicit variants, preferring children composition, and React 19 API updates (ref as normal prop, use() instead of useContext()).
Composition Patterns compiled guide
.agents/skills/vercel-composition-patterns/AGENTS.md
Comprehensive agent-facing guide compiling all composition pattern rules with examples, impact labels, and references.
React Best Practices skill definition
.agents/skills/vercel-react-best-practices/SKILL.md, README.md, metadata.json, rules/_sections.md, rules/_template.md
Introduces 40+ React/Next.js performance rules organized across 8 categories with prioritized impact levels and quick reference.
React Best Practices rules
.agents/skills/vercel-react-best-practices/rules/*.md
40+ rule documents covering advanced patterns, async parallelization, bundle optimization, client performance, JavaScript optimizations, rendering strategies, re-render prevention, and server-side patterns.
Web Design Guidelines skill and infrastructure
.agents/skills/web-design-guidelines/SKILL.md, .claude/skills/* (symlinks), skills-lock.json, .prettierignore
Web design guidelines skill configuration, symlinks for Claude agent access, version-locked skill references, and prettier exclusions for vendored content.

VideoPlayer Component Refactoring

Layer / File(s) Summary
ReactPlayerWrapper component extraction
src/components/VideoPlayer/ReactPlayerWrapper.tsx
New wrapper that default-exports react-player, handles CommonJS/ESM interop differences, and configures poster, controls, sizing.
VideoPlayer lazy loading and Suspense
src/components/VideoPlayer/VideoPlayer.tsx
Updated to lazy-load ReactPlayerWrapper via React.lazy, render inside React.Suspense with context-provided LoadingIndicator, and conditionally render context override.
VideoPlayer tests and styling
src/components/VideoPlayer/__tests__/VideoPlayer.test.tsx, styling/VideoPlayer.scss, styling/index.scss
Added Vitest tests verifying lazy-load behavior and override handling with mocked react-player; added centered flex layout for loading state.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

🐰 A thousand rules of sage advice,
New patterns split and pay the price,
The player lazy-loads with grace,
While loading spinners guard its place! ✨

✨ Finishing Touches
πŸ“ Generate docstrings
  • Create stacked PR
  • Commit on current branch
πŸ§ͺ Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch perf/lazy-video-player

@oliverlaz
oliverlaz merged commit 18dc966 into master Jun 3, 2026
11 of 12 checks passed
@oliverlaz
oliverlaz deleted the perf/lazy-video-player branch June 3, 2026 12:23
github-actions Bot pushed a commit that referenced this pull request Jun 5, 2026
## [14.4.0](v14.3.0...v14.4.0) (2026-06-05)

### Bug Fixes

* **compat:** restore React 17/18 compatibility in certain components ([#3197](#3197)) ([b513b69](b513b69))
* general performance and bug fixes ([#3201](#3201)) ([57c5795](57c5795))
* **interop:** unwrap CJS default exports (react-player, @emoji-mart/react) ([#3199](#3199)) ([4cddb02](4cddb02))
* maintain topmost modal non-inert ([#3206](#3206)) ([7ad98fa](7ad98fa))

### Features

* allow to stack modals on top of each other ([#3203](#3203)) ([4c934ae](4c934ae))
* display notifications above modals ([#3200](#3200)) ([0433090](0433090))
* **Reactions:** send emoji_code with reactions for push notification rendering ([#3209](#3209)) ([2faa620](2faa620))

### Performance Improvements

* **Message:** hoist regex compilation in message text rendering ([#3202](#3202)) ([8c018a4](8c018a4))
* **VideoPlayer:** lazy-load react-player to keep it out of the main bundle ([#3204](#3204)) ([18dc966](18dc966))
@stream-ci-bot

Copy link
Copy Markdown

πŸŽ‰ This PR is included in version 14.4.0 πŸŽ‰

The release is available on:

Your semantic-release bot πŸ“¦πŸš€

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants