diff --git a/packages/react/package.json b/packages/react/package.json
index 97265e36..b28efe09 100644
--- a/packages/react/package.json
+++ b/packages/react/package.json
@@ -16,12 +16,28 @@
"types": "./dist/dev/index.d.ts",
"development": "./dist/dev/index.js",
"default": "./dist/default/index.js"
+ },
+ "./*.css": "./dist/default/presets/*.css",
+ "./audio": {
+ "types": "./dist/dev/presets/audio/index.d.ts",
+ "development": "./dist/dev/presets/audio/index.js",
+ "default": "./dist/default/presets/audio/index.js"
+ },
+ "./background": {
+ "types": "./dist/dev/presets/background/index.d.ts",
+ "development": "./dist/dev/presets/background/index.js",
+ "default": "./dist/default/presets/background/index.js"
+ },
+ "./video": {
+ "types": "./dist/dev/presets/video/index.d.ts",
+ "development": "./dist/dev/presets/video/index.js",
+ "default": "./dist/default/presets/video/index.js"
}
},
"scripts": {
"build": "tsdown",
"build:watch": "tsdown --watch ./src --no-clean",
- "dev": "pnpm run build:watch",
+ "dev": "pnpm build:watch",
"test": "vitest run",
"test:watch": "vitest",
"clean": "rm -rf dist types"
diff --git a/packages/react/src/index.ts b/packages/react/src/index.ts
index f101fa07..db02da9f 100644
--- a/packages/react/src/index.ts
+++ b/packages/react/src/index.ts
@@ -9,8 +9,6 @@ export { createSelector, shallowEqual } from '@videojs/store';
export { useSelector, useStore } from '@videojs/store/react';
// Media primitives
-export { Audio, type AudioProps } from './media/audio';
-export { Video, type VideoProps } from './media/video';
export {
Container,
type ContainerProps,
diff --git a/packages/react/src/presets/audio/index.ts b/packages/react/src/presets/audio/index.ts
new file mode 100644
index 00000000..d8ec0bfc
--- /dev/null
+++ b/packages/react/src/presets/audio/index.ts
@@ -0,0 +1,3 @@
+export { Audio, type AudioProps } from '@/media/audio';
+export * from './minimal-skin';
+export * from './skin';
diff --git a/packages/react/src/presets/audio/minimal-skin.css b/packages/react/src/presets/audio/minimal-skin.css
new file mode 100644
index 00000000..abd1fe89
--- /dev/null
+++ b/packages/react/src/presets/audio/minimal-skin.css
@@ -0,0 +1,3 @@
+.media-minimal-skin {
+ color: var(--media-color, red);
+}
diff --git a/packages/react/src/presets/audio/minimal-skin.tsx b/packages/react/src/presets/audio/minimal-skin.tsx
new file mode 100644
index 00000000..dba6f9eb
--- /dev/null
+++ b/packages/react/src/presets/audio/minimal-skin.tsx
@@ -0,0 +1,8 @@
+import type { BaseSkinProps } from '../types';
+
+export type MinimalAudioSkinProps = BaseSkinProps;
+
+export function MinimalAudioSkin(props: MinimalAudioSkinProps) {
+ const { children } = props;
+ return
{children}
;
+}
diff --git a/packages/react/src/presets/audio/skin.css b/packages/react/src/presets/audio/skin.css
new file mode 100644
index 00000000..9c9762b2
--- /dev/null
+++ b/packages/react/src/presets/audio/skin.css
@@ -0,0 +1,3 @@
+.media-skin {
+ color: var(--media-color, red);
+}
diff --git a/packages/react/src/presets/audio/skin.tsx b/packages/react/src/presets/audio/skin.tsx
new file mode 100644
index 00000000..57228b38
--- /dev/null
+++ b/packages/react/src/presets/audio/skin.tsx
@@ -0,0 +1,8 @@
+import type { BaseSkinProps } from '../types';
+
+export type AudioSkinProps = BaseSkinProps;
+
+export function AudioSkin(props: AudioSkinProps) {
+ const { children } = props;
+ return {children}
;
+}
diff --git a/packages/react/src/presets/background/index.ts b/packages/react/src/presets/background/index.ts
new file mode 100644
index 00000000..966e7046
--- /dev/null
+++ b/packages/react/src/presets/background/index.ts
@@ -0,0 +1 @@
+export * from './skin';
diff --git a/packages/react/src/presets/background/skin.css b/packages/react/src/presets/background/skin.css
new file mode 100644
index 00000000..9c9762b2
--- /dev/null
+++ b/packages/react/src/presets/background/skin.css
@@ -0,0 +1,3 @@
+.media-skin {
+ color: var(--media-color, red);
+}
diff --git a/packages/react/src/presets/background/skin.tsx b/packages/react/src/presets/background/skin.tsx
new file mode 100644
index 00000000..2cdf43ba
--- /dev/null
+++ b/packages/react/src/presets/background/skin.tsx
@@ -0,0 +1,8 @@
+import type { BaseSkinProps } from '../types';
+
+export type BackgroundVideoSkinProps = BaseSkinProps;
+
+export function BackgroundVideoSkin(props: BackgroundVideoSkinProps) {
+ const { children } = props;
+ return {children}
;
+}
diff --git a/packages/react/src/presets/types.ts b/packages/react/src/presets/types.ts
new file mode 100644
index 00000000..5ace3e65
--- /dev/null
+++ b/packages/react/src/presets/types.ts
@@ -0,0 +1,3 @@
+import type { PropsWithChildren } from 'react';
+
+export type BaseSkinProps = PropsWithChildren;
diff --git a/packages/react/src/presets/video/index.ts b/packages/react/src/presets/video/index.ts
new file mode 100644
index 00000000..93f7dd44
--- /dev/null
+++ b/packages/react/src/presets/video/index.ts
@@ -0,0 +1,3 @@
+export { Video, type VideoProps } from '@/media/video';
+export * from './minimal-skin';
+export * from './skin';
diff --git a/packages/react/src/presets/video/minimal-skin.css b/packages/react/src/presets/video/minimal-skin.css
new file mode 100644
index 00000000..abd1fe89
--- /dev/null
+++ b/packages/react/src/presets/video/minimal-skin.css
@@ -0,0 +1,3 @@
+.media-minimal-skin {
+ color: var(--media-color, red);
+}
diff --git a/packages/react/src/presets/video/minimal-skin.tsx b/packages/react/src/presets/video/minimal-skin.tsx
new file mode 100644
index 00000000..f1d19cc5
--- /dev/null
+++ b/packages/react/src/presets/video/minimal-skin.tsx
@@ -0,0 +1,8 @@
+import type { BaseSkinProps } from '../types';
+
+export type MinimalVideoSkinProps = BaseSkinProps;
+
+export function MinimalVideoSkin(props: MinimalVideoSkinProps) {
+ const { children } = props;
+ return {children}
;
+}
diff --git a/packages/react/src/presets/video/skin.css b/packages/react/src/presets/video/skin.css
new file mode 100644
index 00000000..9c9762b2
--- /dev/null
+++ b/packages/react/src/presets/video/skin.css
@@ -0,0 +1,3 @@
+.media-skin {
+ color: var(--media-color, red);
+}
diff --git a/packages/react/src/presets/video/skin.tsx b/packages/react/src/presets/video/skin.tsx
new file mode 100644
index 00000000..f183d9d1
--- /dev/null
+++ b/packages/react/src/presets/video/skin.tsx
@@ -0,0 +1,8 @@
+import type { BaseSkinProps } from '../types';
+
+export type VideoSkinProps = BaseSkinProps;
+
+export function VideoSkin(props: VideoSkinProps) {
+ const { children } = props;
+ return {children}
;
+}
diff --git a/packages/react/tsdown.config.ts b/packages/react/tsdown.config.ts
index 6df8c8f1..faedff72 100644
--- a/packages/react/tsdown.config.ts
+++ b/packages/react/tsdown.config.ts
@@ -1,3 +1,4 @@
+import { globSync } from 'node:fs';
import type { UserConfig } from 'tsdown';
import { defineConfig } from 'tsdown';
@@ -6,9 +7,7 @@ type BuildMode = 'dev' | 'default';
const buildModes: BuildMode[] = ['dev', 'default'];
const createConfig = (mode: BuildMode): UserConfig => ({
- entry: {
- index: './src/index.ts',
- },
+ entry: 'src/**/index.ts',
platform: 'browser',
format: 'es',
sourcemap: true,
@@ -23,6 +22,24 @@ const createConfig = (mode: BuildMode): UserConfig => ({
__DEV__: mode === 'dev' ? 'true' : 'false',
},
dts: mode === 'dev',
+ copy: [
+ {
+ from: 'src/**/*.css',
+ to: `dist/${mode}`,
+ flatten: false,
+ },
+ ],
+ plugins: [
+ {
+ name: 'watch-css',
+ buildStart() {
+ const cssFiles = globSync('src/**/*.css');
+ for (const file of cssFiles) {
+ this.addWatchFile(file);
+ }
+ },
+ },
+ ],
});
export default defineConfig(buildModes.map((mode) => createConfig(mode)));
diff --git a/packages/sandbox/templates/react/main.tsx b/packages/sandbox/templates/react/main.tsx
index 7e67d936..39ab2b32 100644
--- a/packages/sandbox/templates/react/main.tsx
+++ b/packages/sandbox/templates/react/main.tsx
@@ -1,7 +1,8 @@
// React sandbox — React player
// http://localhost:5173/react/
-import { Container, createPlayer, features, Video } from '@videojs/react';
+import { Container, createPlayer, features } from '@videojs/react';
+import { Video } from '@videojs/react/video';
import { createRoot } from 'react-dom/client';
const { Provider } = createPlayer({
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index cd627781..8bef7d7b 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -9899,7 +9899,7 @@ snapshots:
sirv: 3.0.2
tinyglobby: 0.2.15
tinyrainbow: 2.0.0
- vitest: 3.2.4(@types/debug@4.1.12)(@types/node@22.19.3)(@vitest/ui@3.2.4)(happy-dom@18.0.1)(jiti@2.6.1)(jsdom@27.3.0(postcss@8.5.6))(lightningcss@1.30.2)(tsx@4.21.0)(yaml@2.8.2)
+ vitest: 3.2.4(@types/debug@4.1.12)(@types/node@22.19.3)(@vitest/ui@3.2.4)(happy-dom@18.0.1)(jiti@2.6.1)(jsdom@26.1.0)(lightningcss@1.30.2)(tsx@4.21.0)(yaml@2.8.2)
'@vitest/utils@3.2.4':
dependencies:
diff --git a/site/src/components/docs/demos/controls/react/css/BasicUsage.tsx b/site/src/components/docs/demos/controls/react/css/BasicUsage.tsx
index 98be27fb..c98d2cfa 100644
--- a/site/src/components/docs/demos/controls/react/css/BasicUsage.tsx
+++ b/site/src/components/docs/demos/controls/react/css/BasicUsage.tsx
@@ -1,4 +1,5 @@
-import { Controls, createPlayer, features, PlayButton, Time, Video } from '@videojs/react';
+import { Controls, createPlayer, features, PlayButton, Time } from '@videojs/react';
+import { Video } from '@videojs/react/video';
import './BasicUsage.css';
diff --git a/site/src/components/docs/demos/fullscreen-button/react/css/BasicUsage.tsx b/site/src/components/docs/demos/fullscreen-button/react/css/BasicUsage.tsx
index ac813524..5da6e275 100644
--- a/site/src/components/docs/demos/fullscreen-button/react/css/BasicUsage.tsx
+++ b/site/src/components/docs/demos/fullscreen-button/react/css/BasicUsage.tsx
@@ -1,4 +1,5 @@
-import { createPlayer, FullscreenButton, features, Video } from '@videojs/react';
+import { createPlayer, FullscreenButton, features } from '@videojs/react';
+import { Video } from '@videojs/react/video';
import './BasicUsage.css';
diff --git a/site/src/components/docs/demos/mute-button/react/css/BasicUsage.tsx b/site/src/components/docs/demos/mute-button/react/css/BasicUsage.tsx
index 59ca479c..5d34a53d 100644
--- a/site/src/components/docs/demos/mute-button/react/css/BasicUsage.tsx
+++ b/site/src/components/docs/demos/mute-button/react/css/BasicUsage.tsx
@@ -1,4 +1,5 @@
-import { createPlayer, features, MuteButton, Video } from '@videojs/react';
+import { createPlayer, features, MuteButton } from '@videojs/react';
+import { Video } from '@videojs/react/video';
import './BasicUsage.css';
diff --git a/site/src/components/docs/demos/play-button/react/css/BasicUsage.tsx b/site/src/components/docs/demos/play-button/react/css/BasicUsage.tsx
index d2575072..9a815419 100644
--- a/site/src/components/docs/demos/play-button/react/css/BasicUsage.tsx
+++ b/site/src/components/docs/demos/play-button/react/css/BasicUsage.tsx
@@ -1,4 +1,5 @@
-import { createPlayer, features, PlayButton, Video } from '@videojs/react';
+import { createPlayer, features, PlayButton } from '@videojs/react';
+import { Video } from '@videojs/react/video';
import './BasicUsage.css';
diff --git a/site/src/components/docs/demos/poster/react/css/BasicUsage.tsx b/site/src/components/docs/demos/poster/react/css/BasicUsage.tsx
index c29c77b7..532e2835 100644
--- a/site/src/components/docs/demos/poster/react/css/BasicUsage.tsx
+++ b/site/src/components/docs/demos/poster/react/css/BasicUsage.tsx
@@ -1,4 +1,5 @@
-import { createPlayer, features, PlayButton, Poster, Video } from '@videojs/react';
+import { createPlayer, features, PlayButton, Poster } from '@videojs/react';
+import { Video } from '@videojs/react/video';
import './BasicUsage.css';
diff --git a/site/src/components/docs/demos/time/react/css/CurrentDuration.tsx b/site/src/components/docs/demos/time/react/css/CurrentDuration.tsx
index c2af6f5c..b815fd53 100644
--- a/site/src/components/docs/demos/time/react/css/CurrentDuration.tsx
+++ b/site/src/components/docs/demos/time/react/css/CurrentDuration.tsx
@@ -1,4 +1,5 @@
-import { createPlayer, features, Time, Video } from '@videojs/react';
+import { createPlayer, features, Time } from '@videojs/react';
+import { Video } from '@videojs/react/video';
import './CurrentDuration.css';
diff --git a/site/src/components/docs/demos/time/react/css/CurrentTime.tsx b/site/src/components/docs/demos/time/react/css/CurrentTime.tsx
index de72aee0..1f8d1c6a 100644
--- a/site/src/components/docs/demos/time/react/css/CurrentTime.tsx
+++ b/site/src/components/docs/demos/time/react/css/CurrentTime.tsx
@@ -1,4 +1,5 @@
-import { createPlayer, features, Time, Video } from '@videojs/react';
+import { createPlayer, features, Time } from '@videojs/react';
+import { Video } from '@videojs/react/video';
import './CurrentTime.css';
diff --git a/site/src/components/docs/demos/time/react/css/CustomNegativeSign.tsx b/site/src/components/docs/demos/time/react/css/CustomNegativeSign.tsx
index 7b00f6a9..fd7b423d 100644
--- a/site/src/components/docs/demos/time/react/css/CustomNegativeSign.tsx
+++ b/site/src/components/docs/demos/time/react/css/CustomNegativeSign.tsx
@@ -1,4 +1,5 @@
-import { createPlayer, features, Time, Video } from '@videojs/react';
+import { createPlayer, features, Time } from '@videojs/react';
+import { Video } from '@videojs/react/video';
import './CustomNegativeSign.css';
diff --git a/site/src/components/docs/demos/time/react/css/CustomSeparator.tsx b/site/src/components/docs/demos/time/react/css/CustomSeparator.tsx
index f9868bf2..66f462a0 100644
--- a/site/src/components/docs/demos/time/react/css/CustomSeparator.tsx
+++ b/site/src/components/docs/demos/time/react/css/CustomSeparator.tsx
@@ -1,4 +1,5 @@
-import { createPlayer, features, Time, Video } from '@videojs/react';
+import { createPlayer, features, Time } from '@videojs/react';
+import { Video } from '@videojs/react/video';
import './CustomSeparator.css';
diff --git a/site/src/components/docs/demos/time/react/css/Remaining.tsx b/site/src/components/docs/demos/time/react/css/Remaining.tsx
index f2714ba1..b9421167 100644
--- a/site/src/components/docs/demos/time/react/css/Remaining.tsx
+++ b/site/src/components/docs/demos/time/react/css/Remaining.tsx
@@ -1,4 +1,5 @@
-import { createPlayer, features, Time, Video } from '@videojs/react';
+import { createPlayer, features, Time } from '@videojs/react';
+import { Video } from '@videojs/react/video';
import './Remaining.css';