Motion
Lazy video
A muted loop that loads near the viewport and plays only on screen.
stablev0.1.0 Chromium 103 ui-scale safe
Install
Copies the file into
components/ds/, with its registry dependencies and npm packages. See Installation for the one-time setup.bunx shadcn@3 add @fastrp/lazy-videoExamples
Default
Karakterler
Satranç
API
<LazyVideo />A muted loop that loads when it is about to be seen and plays only while it is on screen, so a page of clips costs one or two decoders at a time. It covers its box: size the parent.
| Prop | Type | Default | Notes |
|---|---|---|---|
| src* | string | — | |
| poster | string | — | Shown until the clip loads, and for good if autoplay is blocked. |
Dependencies
npm
- motion@^12.40.0
Registry
- @fastrp/scroll-root
- @fastrp/utils
Source
components/ds/lazy-video.tsxShowHide
'use client';
import { useInView } from 'motion/react';
import type * as React from 'react';
import { useEffect, useRef } from 'react';
import { useScrollRoot } from '@/components/ds/scroll-root';
import { cn } from '@/lib/utils';
// @fast-ds [email protected]
export type LazyVideoProps = Omit<
React.ComponentProps<'video'>,
'src' | 'autoPlay' | 'muted' | 'loop' | 'preload'
> & {
src: string;
/** Shown until the clip loads, and for good if autoplay is blocked. */
poster?: string;
};
/**
* A muted loop that loads when it is about to be seen and plays only while it is on screen, so a
* page of clips costs one or two decoders at a time. It covers its box: size the parent.
*/
function LazyVideo({ src, poster, className, ...props }: LazyVideoProps) {
const ref = useRef<HTMLVideoElement>(null);
const root = useScrollRoot();
const near = useInView(ref, { once: true, margin: '50% 0px 50% 0px', root });
const visible = useInView(ref, { amount: 0.25, root });
useEffect(() => {
const video = ref.current;
if (!video) return;
// A blocked autoplay leaves the poster up; nothing to recover.
if (visible) video.play().catch(() => {});
else video.pause();
}, [visible]);
return (
<video
ref={ref}
data-slot="lazy-video"
src={near ? src : undefined}
poster={poster}
muted
loop
playsInline
preload={near ? 'auto' : 'none'}
className={cn('size-full object-cover', className)}
{...props}
/>
);
}
export { LazyVideo };