React
Add a scroll video to any React 18 or 19 app.
vidscroll works with React 18 and 19 in any bundler setup, such as Vite, Next.js or Astro. Its one dependency, the video library it uses to read and re-encode files, is loaded in the browser only when a video needs it.
Set it up
-
Install vidscroll.
npm install vidscrollpnpm add vidscrollyarn add vidscrollbun add vidscroll -
Put a video in your public folder. Any MP4 or WebM works. In most setups,
public/videos/hero.mp4is served at/videos/hero.mp4. -
Add
ScrollVideowhere the video should appear.Hero.tsximport { ScrollVideo } from "vidscroll"; export function Hero() { return <ScrollVideo src="/videos/hero.mp4" length="400vh" />; }
That’s a working scroll video. The rest of this page covers showing content over it, how it sits in your layout, and preparing the file for production.
Show content over the video
Children of ScrollVideo are drawn over the video. Wrap them in Section to
show them over part of the scroll:
import { ScrollVideo, Section } from "vidscroll";
export function Hero() {
return (
<ScrollVideo src="/videos/hero.mp4" length="400vh">
<Section start={0} end={0.3}>
<h2>Doors close</h2>
</Section>
<Section start={0.55} end={0.9}>
<h2>Open water</h2>
</Section>
</ScrollVideo>
);
}import { ScrollVideo, Section } from "vidscroll";
export default function Example() {
return (
<ScrollVideo src="/train.mp4" length="400vh">
<Section id="doors" start={0} end={0.3}>
<h2>Doors close</h2>
</Section>
<Section id="water" start={0.55} end={0.9}>
<h2>Open water</h2>
</Section>
</ScrollVideo>
);
}start and end are positions in the scroll, from 0 to 1. Sections can also
use seconds (fromTime, toTime) or frame numbers. Content outside a
Section stays on screen the whole time.
How it sits in your page
ScrollVideo renders a full-width block. Its height is length plus one
screen: with length="400vh", the block is five screens tall, and the video is
pinned for the first four while the page scrolls past. Content before and after
it scrolls normally, and a page can hold several scroll videos.
Set length explicitly when content follows the video. Then the block has its
full height from the first render (on the server too), and nothing below it
moves. With the default "auto", the length comes from the video’s duration,
which is only known once it loads, so anything below moves down at that point.
What visitors see while it loads
| Moment | On screen |
|---|---|
| Downloading | The video’s first frame behind a loading bar, or your poster image. |
| Preparing | A short pass over the timeline so later jumps are fast. |
| Re-encoding (first visit only) | The original video, already scrollable, with a small progress badge in the corner. Only for files that aren’t prepared. |
| Ready | The video, following the scroll. |
All of this can be customised or turned off with the loader and poster
props. See ScrollVideo.
Prepare the video for production
Smooth scrubbing needs keyframes close together, and most exported videos have them seconds apart (why). vidscroll fixes that in the visitor’s browser on their first visit, but that costs them a few seconds. Preparing the file once, before you deploy, skips that step for everyone.
The vidscroll encode command does it. It uses ffmpeg, the standard
command-line video tool, which vidscroll doesn’t bundle because it’s a large
native program. The easiest way to get it is the ffmpeg-static package. It’s
a development dependency: it runs on your machine or in CI and is never sent to
visitors.
-
Get ffmpeg. Skip this if
ffmpegalready works in your terminal.npm install -D ffmpeg-staticpnpm add -D ffmpeg-staticyarn add -D ffmpeg-staticbun add -d ffmpeg-static -
Prepare the file.
Terminalnpx vidscroll encode public/videos/hero.mp4 -
Use the prepared file. The command writes
hero.scroll.mp4next to the original.Hero.tsx<ScrollVideo src="/videos/hero.scroll.mp4" length="400vh" />
The prepared file has a keyframe every 10 frames, is at most 720p on its short
side, has no audio track, and is set up to start playing before it finishes
downloading. Options such as --resolution 1080 are listed in the
CLI reference.
Next steps
- ScrollVideo: every option, with live examples.
- Next.js: server and client components.