diff --git a/packages/html/src/define/media/background-video.ts b/packages/html/src/define/media/background-video.ts
new file mode 100644
index 00000000..01ee8581
--- /dev/null
+++ b/packages/html/src/define/media/background-video.ts
@@ -0,0 +1,13 @@
+import { BackgroundVideo } from '../../media/background-video';
+
+export class BackgroundVideoElement extends BackgroundVideo {
+ static readonly tagName = 'background-video';
+}
+
+customElements.define(BackgroundVideoElement.tagName, BackgroundVideoElement);
+
+declare global {
+ interface HTMLElementTagNameMap {
+ [BackgroundVideoElement.tagName]: BackgroundVideoElement;
+ }
+}
diff --git a/packages/html/src/define/media/hls-video.ts b/packages/html/src/define/media/hls-video.ts
index 5c80ba88..2b8e3780 100644
--- a/packages/html/src/define/media/hls-video.ts
+++ b/packages/html/src/define/media/hls-video.ts
@@ -1,4 +1,4 @@
-import { HlsVideo } from '../../ui/hls-video/hls-video-element';
+import { HlsVideo } from '../../media/hls-video';
export class HlsVideoElement extends HlsVideo {
static readonly tagName = 'hls-video';
diff --git a/packages/html/src/media/background-video/index.ts b/packages/html/src/media/background-video/index.ts
new file mode 100644
index 00000000..3f44da5a
--- /dev/null
+++ b/packages/html/src/media/background-video/index.ts
@@ -0,0 +1,80 @@
+import { CustomMediaMixin } from '../custom-media-element';
+
+function getTemplateHTML(attrs: Record) {
+ return /*html*/ `
+
+
+
+ `;
+}
+
+export class BackgroundVideo extends CustomMediaMixin(HTMLElement, { tag: 'video' }) {
+ static shadowRootOptions = { mode: 'open' as ShadowRootMode };
+ static getTemplateHTML = getTemplateHTML;
+
+ constructor() {
+ super();
+
+ if (!this.shadowRoot) {
+ this.attachShadow((this.constructor as typeof BackgroundVideo).shadowRootOptions);
+
+ const attrs = {
+ ...namedNodeMapToObject(this.attributes),
+ ...(!this.hasAttribute('nomuted') && { muted: '' }),
+ ...(!this.hasAttribute('noloop') && { loop: '' }),
+ ...(!this.hasAttribute('noautoplay') && { autoplay: '' }),
+ playsinline: '',
+ disableremoteplayback: '',
+ disablepictureinpicture: '',
+ };
+
+ this.shadowRoot!.innerHTML = getTemplateHTML(attrs);
+ }
+ }
+}
+
+function namedNodeMapToObject(namedNodeMap: NamedNodeMap) {
+ const obj: Record = {};
+ for (const attr of namedNodeMap) {
+ obj[attr.name] = attr.value;
+ }
+ return obj;
+}
+
+const VideoAttributes = [
+ 'autoplay',
+ 'controls',
+ 'controlslist',
+ 'crossorigin',
+ 'disablepictureinpicture',
+ 'disableremoteplayback',
+ 'loop',
+ 'muted',
+ 'playsinline',
+ 'preload',
+] as const;
+
+function serializeAttributes(attrs: Record): string {
+ let html = '';
+ for (const key in attrs) {
+ // Skip forwarding non native video attributes.
+ if (!VideoAttributes.includes(key as any)) continue;
+
+ const value = attrs[key];
+ if (value === '') html += ` ${key}`;
+ else html += ` ${key}="${value}"`;
+ }
+ return html;
+}
diff --git a/packages/html/src/ui/custom-media-element.ts b/packages/html/src/media/custom-media-element.ts
similarity index 100%
rename from packages/html/src/ui/custom-media-element.ts
rename to packages/html/src/media/custom-media-element.ts
diff --git a/packages/html/src/ui/hls-video/hls-video-element.ts b/packages/html/src/media/hls-video/index.ts
similarity index 100%
rename from packages/html/src/ui/hls-video/hls-video-element.ts
rename to packages/html/src/media/hls-video/index.ts
diff --git a/packages/react/src/media/background-video/index.tsx b/packages/react/src/media/background-video/index.tsx
new file mode 100644
index 00000000..48f249a7
--- /dev/null
+++ b/packages/react/src/media/background-video/index.tsx
@@ -0,0 +1,35 @@
+'use client';
+
+import type { VideoHTMLAttributes } from 'react';
+import { forwardRef, useCallback } from 'react';
+
+import { useMediaRegistration } from '../../player/context';
+import { useComposedRefs } from '../../utils/use-composed-refs';
+
+export interface BackgroundVideoProps extends VideoHTMLAttributes {}
+
+export const BackgroundVideo = forwardRef(function BackgroundVideo(
+ { children, ...props },
+ ref
+) {
+ const setMedia = useMediaRegistration();
+
+ const mediaRef = useCallback(
+ (el: HTMLVideoElement | null) => {
+ setMedia?.(el);
+ },
+ [setMedia]
+ );
+
+ const composedRef = useComposedRefs(ref, mediaRef);
+
+ return (
+
+ );
+});
+
+export namespace BackgroundVideo {
+ export type Props = BackgroundVideoProps;
+}
diff --git a/packages/react/src/presets/background/index.ts b/packages/react/src/presets/background/index.ts
index 966e7046..ead7edd5 100644
--- a/packages/react/src/presets/background/index.ts
+++ b/packages/react/src/presets/background/index.ts
@@ -1 +1,2 @@
+export { BackgroundVideo, type BackgroundVideoProps } from '@/media/background-video';
export * from './skin';