Building a Snap Scrolling Reel-Like Video App with React and react-window

Shehzad Ahmed
3 min readJul 2, 2024

--

In this tutorial, we’ll build a snap scrolling component for a reel-like video app using React and the react-window package. Our component will show one video at a time, with smooth snapping between videos and detect when the last video is visible.

Building a Snap Scrolling Reel-Like Video App with React and react-window

Step 1: Setting Up the Project

First, ensure you have Node.js installed. Create a new React project and install the necessary package:

npm install react-window

Step 2: Creating the VideoItem Component

The VideoItem component will handle the display and playback of individual videos. It includes play/pause functionality and a loading indicator.

import React, { useState, useRef, useEffect } from "react";

const VideoItem = ({ style, video }) => {
const ref = useRef();
const [isPlaying, setIsPlaying] = useState(false);
const [isLoaded, setIsLoaded] = useState(false);
useEffect(() => {
const videoRef = ref.current;
if (!videoRef) return;
const handlePlay = () => setIsPlaying(true);
const handlePause = () => setIsPlaying(false);
videoRef.addEventListener("play", handlePlay);
videoRef.addEventListener("pause", handlePause);
videoRef.addEventListener("loadeddata", () => setIsLoaded(true));
return () => {
videoRef.removeEventListener("play", handlePlay);
videoRef.removeEventListener("pause", handlePause);
};
}, []);
return (
<div
style={{
...style,
display: "flex",
alignItems: "center",
justifyContent: "center",
}}
>
{!isPlaying && (
<button
style={{ position: "absolute", zIndex: 1 }}
onClick={() => ref.current.play()}
>
Play
</button>
)}
{!isLoaded && (
<p style={{ position: "absolute", zIndex: 1 }}>Loading...</p>
)}
<video
src={video}
ref={ref}
autoPlay
style={{ width: "100%", height: "100%" }}
onEnded={(e) => console.log(e, "Ended")}
onClick={(e) => {
ref.current.pause();
console.log(e, "onClick");
}}
/>
</div>
);
};
export default VideoItem;

Step 3: Creating the SnapScroll Component

The SnapScroll component uses react-window to efficiently render a large list of videos. It also includes logic to detect when the last video is visible.

import React, { useState, useRef, useCallback, useEffect } from "react";
import { FixedSizeList as List } from "react-window";
import VideoItem from "./VideoItem";

const SnapScroll = ({ onLastVideo, videos }) => {
const listRef = useRef();
const [isLastVideoVisible, setIsLastVideoVisible] = useState(false);
const itemCount = videos.length;
const itemSize = 650; // height of each video item
const width = 500; // width of each video item
const handleScroll = useCallback(() => {
if (!listRef.current) return;
const { scrollTop, clientHeight } = listRef.current._outerRef;
const lastItemOffset = itemSize * (itemCount - 1);
const maxScrollTop = lastItemOffset - clientHeight;
if (scrollTop >= maxScrollTop) {
setIsLastVideoVisible(true);
} else {
setIsLastVideoVisible(false);
}
}, [itemCount, itemSize]);
useEffect(() => {
const ref = listRef.current;
if (ref) {
ref._outerRef.addEventListener("scroll", handleScroll);
}
return () => {
if (ref) {
ref._outerRef.removeEventListener("scroll", handleScroll);
}
};
}, [handleScroll]);
useEffect(() => {
if (isLastVideoVisible) {
onLastVideo();
}
}, [isLastVideoVisible, onLastVideo]);
return (
<div>
<List
ref={listRef}
height={itemSize}
itemCount={itemCount}
itemSize={itemSize}
width={width}
style={{ scrollSnapType: "y mandatory", overflowY: "scroll" }}
>
{({ index, style }) => (
<div
style={{
...style,
scrollSnapAlign: "start",
scrollSnapStop: "always",
}}
>
<VideoItem
index={index}
style={{ width, height: itemSize }}
video={videos[index]}
/>
</div>
)}
</List>
{isLastVideoVisible && <div>Last video is visible</div>}
</div>
);
};
export default SnapScroll;

Step 4: Integrating the SnapScroll Component

Now, integrate the SnapScroll component into your main application and pass a list of video URLs.

import React from "react";
import ReactDOM from "react-dom";
import SnapScroll from "./SnapScroll";

const videos = [
'https://www.w3schools.com/html/mov_bbb.mp4',
'https://www.w3schools.com/html/movie.mp4',
// Add more video URLs here
];
const App = () => {
const handleLastVideo = () => {
console.log("Last video is visible");
};
return (
<div>
<h1>Reel App</h1>
<SnapScroll videos={videos} onLastVideo={handleLastVideo} />
</div>
);
};
ReactDOM.render(<App />, document.getElementById("root"));

Conclusion

In this tutorial, we built a snap scrolling reel-like video app using React and react-window. We created a VideoItem component to handle video playback and a SnapScroll component to handle the snapping behavior and detect when the last video is visible. This setup ensures efficient rendering and smooth scrolling, providing a seamless user experience similar to popular social media reels.

Reference Links

  • Github Repo — Complete implementation of this feature in a basic react app

Find me on your favorite platform

  • Github — Follow me on GitHub for further useful code snippets and open source repos.
  • LinkedIn Profile — Connect with me on LinkedIn for further discussions and updates.
  • Twitter (X) — Connect with me on Twitter (X) for useless tech tweets.

--

--

Responses (1)