Next.js
Use vidscroll in the App Router, from server components or client components.
Set it up
-
Install vidscroll.
npm install vidscrollpnpm add vidscrollyarn add vidscrollbun add vidscroll -
Put the video in
public/. A file atpublic/videos/hero.mp4is served at/videos/hero.mp4. -
Use
ScrollVideoin a page. No"use client"needed:app/page.tsximport { ScrollVideo, Section } from "vidscroll"; export default function Page() { return ( <main> <ScrollVideo src="/videos/hero.mp4" length="400vh" easing="inOutSine"> <Section start={0} end={0.3}> <h2>Doors close</h2> </Section> </ScrollVideo> </main> ); }
Server component or client component?
Both work, and the video behaves exactly the same either way. vidscroll’s
components are marked "use client" inside the package, so when you use them
from a server component, Next.js treats them as client components
automatically. What changes is only what you can pass to them.
| From a server component | From your own client component | |
|---|---|---|
| Rendered on the server | Yes | Yes, Next.js renders client components on the server too |
| Where the video runs | In the browser, after the page loads | The same |
| Props you can pass | Plain data: strings, numbers, booleans, objects | Anything, including functions |
| Hooks | Not available | Available inside ScrollVideo |
| Scrolling and loading | Identical | Identical |
So start in a server component, and move to a client component only when you need one of these:
- a custom
easingfunction (a preset name such as"inOutSine"works anywhere) onLoad,onErroror aloaderfunction- the hooks
useScrollVideo,useScrollVideoStateoruseScrollVideoUpdate
Using functions and hooks
Put the scroll video in your own component that starts with "use client",
then use that component from any page:
"use client";
import { ScrollVideo, Section, useScrollVideoState } from "vidscroll";
function Timecode() {
const { time } = useScrollVideoState();
return <p>{time.toFixed(1)} s</p>;
}
export function Hero() {
return (
<ScrollVideo
src="/videos/hero.mp4"
length="400vh"
easing={(t) => t * t}
onLoad={({ source }) => console.log("served as", source)}
>
<Section start={0} end={1}>
<Timecode />
</Section>
</ScrollVideo>
);
}import { Hero } from "./hero";
export default function Page() {
return <Hero />;
}Hooks work in any component rendered inside ScrollVideo, like Timecode
above.
What the server sends
Nothing video-related happens on the server: the file is never fetched there.
The server sends the markup for the block, the first frame (or your poster)
and the loading bar. In the browser, React takes over and vidscroll starts
downloading the video.
With an explicit length such as "400vh", the server already sends the
block at its full height, so nothing on the page moves when the video starts.
Next steps
- ScrollVideo: every option, with live examples.
- How it works: loading, keyframes and re-encoding.