Skip to content

fix(docs): split ejected React skin sample into player + component files#1588

Open
R-Delfino95 wants to merge 2 commits into
videojs:mainfrom
R-Delfino95:fix/ejected-react-skin-fast-refresh-1484
Open

fix(docs): split ejected React skin sample into player + component files#1588
R-Delfino95 wants to merge 2 commits into
videojs:mainfrom
R-Delfino95:fix/ejected-react-skin-fast-refresh-1484

Conversation

@R-Delfino95

@R-Delfino95 R-Delfino95 commented May 22, 2026

Copy link
Copy Markdown
Collaborator

fix(docs): split ejected React skin sample into player + component files

Closes #1484

Summary

  • Split the ejected React skin sample into player.ts (owns the createPlayer({ features }) call) and VideoPlayer.tsx / AudioPlayer.tsx (owns the component). Co-locating both in one file made React Fast Refresh bail out — Fast Refresh requires a file to export only React components, and the Player const returned by createPlayer violated that rule.
  • EjectedSkin.astro now renders one tab per file (component file first, player.ts second, CSS last). Filename → Shiki language is inferred from the extension so .ts files highlight as TS instead of TSX.
  • No changes to the actual skin sources in packages/react/src/presets/** — those already export only the component. This is purely a build-output + docs-rendering fix.
  • Companion issues [Docs] Ejected skin sample uses any type, should be unknown #1485 (anyunknown) and [Docs] Next.js ejected skin sample missing 'use client' directive #1486 ('use client' directive) are already closed, so this PR is scoped strictly to Bug: Ejected Skin Sample Causes Fast Refresh Warning in React #1484.

Changes

Build script (site/scripts/build-ejected-skins.ts)

  • flattenSkinIntoPlayer now returns { player: string; component: string } instead of a single concatenated string. The player content is a static template with the two imports and the createPlayer call; the component content carries 'use client', the rest of the imports plus import { Player } from './player';, and the JSX component.
  • processReactSkin returns { tsx: Record<string, string>; jsx: Record<string, string> } keyed by filename (player.ts + VideoPlayer.tsx for the TSX variant; player.js + VideoPlayer.jsx for the JSX variant). tsxToJsx runs per-file.
  • EjectedSkinEntry.tsx/jsx updated to Record<string, string> to match.

Content collection schema (site/src/content.config.ts)

  • ejectedSkins.tsx/jsx Zod schema changed from z.string().optional() to z.record(z.string(), z.string()).optional().

Docs rendering (site/src/components/docs/EjectedSkin.astro)

  • Iterates Object.entries(skin.tsx) to build one <Tab> + <TabsPanel> per file. Component files (*.tsx / *.jsx) sort first so they're the initial tab. The CSS tab stays at the end.
  • lang for <ServerCode> is inferred from the filename extension via a small EXT_TO_LANG map (ts'ts', tsx'tsx', etc.).

Home demo (site/src/components/home/Demo/Demo.astro)

  • The "Take full control" teaser card now reads defaultVideoReact.tsx!['VideoPlayer.tsx']! (the component file only) instead of the whole TSX string. Visually unchanged for end users — still a single code block.

E2E sync (apps/e2e/scripts/sync-ejected-skins.ts)

  • Updated to consume the new tsx record shape. Writes both player.ts and ejected-react-video-skin.tsx (component renamed to keep the existing import path in generate-pages.ts stable). The component file's import { Player } from './player' resolves to the sibling player.ts.

Testing

  • pnpm -F site run ejected-skins → 16 entries written. default-video-react.tsx has keys ['player.ts', 'VideoPlayer.tsx']. player.ts only contains the two imports + export const Player; VideoPlayer.tsx opens with 'use client', imports Player from ./player, and createPlayer appears 0 times in the component file. Same shape for audio, minimal, and Tailwind variants.
  • pnpm -F @videojs/e2e sync-ejected-skins → writes player.ts, ejected-react-video-skin.tsx, and player.css to apps/e2e/apps/vite/src/_generated/.
  • pnpm -F site astro check → 0 errors, 0 warnings.
  • pnpm -F site test → 18 files, 397 tests pass.
  • pnpm -F site dev/docs/framework/react/how-to/customize-skins renders two tabs (VideoPlayer.tsx initial + player.ts) plus the existing CSS tab for each React skin.
  • Manual Fast Refresh smoke test in a scratch Vite+React project — paste both files, edit the component, confirm HMR applies without a full reload.

Note

Low Risk
Docs-site build, content schema, and e2e fixture sync only; runtime player packages are untouched, with the main operational risk being regenerating ejected-skins.json and downstream consumers of the old string tsx shape.

Overview
Ejected React skin build output is no longer one monolithic TSX string. flattenSkinIntoPlayer now emits player.ts (createPlayer + Player export) and VideoPlayer.tsx / AudioPlayer.tsx (component with import { Player } from './player'), and processReactSkin stores tsx / jsx as Record<filename, source> (JSX variant uses player.js + .jsx).

Consumers were aligned to that shape: the ejectedSkins Zod schema uses z.record for tsx/jsx; EjectedSkin.astro shows one tab per file (component first) with Shiki lang from the extension; the home Demo.astro teaser uses only VideoPlayer.tsx; sync-ejected-skins.ts writes both player.ts and the renamed component file so ./player resolves in e2e.

No changes to preset sources under packages/react—this is build output and docs/e2e wiring for #1484 (Fast Refresh bails when createPlayer shares a file with React components).

Reviewed by Cursor Bugbot for commit 49d7b32. Bugbot is set up for automated code reviews on this repo. Configure here.

The ejected React skin sample co-located the createPlayer({ features })
call and the React component in a single file. React Fast Refresh
requires a file to export only components — the Player const returned
by createPlayer broke that rule and caused HMR to bail out for users
copy-pasting the sample.

Split the output into player.ts (owns createPlayer) and
VideoPlayer.tsx / AudioPlayer.tsx (owns the component). The build
script now emits a Record<filename, source> per skin; EjectedSkin.astro
renders one tab per file with the component as the initial tab. The
e2e sync script and home-page demo were updated to read the new shape.

Closes videojs#1484

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@netlify

netlify Bot commented May 22, 2026

Copy link
Copy Markdown

Deploy Preview for vjs10-site ready!

Name Link
🔨 Latest commit 49d7b32
🔍 Latest deploy log https://app.netlify.com/projects/vjs10-site/deploys/6a455a91580ebc0008362153
😎 Deploy Preview https://deploy-preview-1588--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 May 22, 2026

Copy link
Copy Markdown

@R-Delfino95 is attempting to deploy a commit to the Mux Team on Vercel.

A member of the Team first needs to authorize it.

@vercel

vercel Bot commented Jul 1, 2026

Copy link
Copy Markdown

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

Project Deployment Actions Updated (UTC)
v10-sandbox Ready Ready Preview, Comment Jul 1, 2026 6:22pm

Request Review

@decepulis decepulis left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I haven't verified the problem, but the code implemented here is great. The only thing I want to see is a JS tab on the home page so that example's not incomplete.

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.

Bug: Ejected Skin Sample Causes Fast Refresh Warning in React

3 participants