When I needed a smooth, high-performance vertical video feed in a React web project, I was surprised by how few complete solutions existed.
I found some great inspiration from this awesome React Native tutorial by @albertocabrerajr, which showed how to build a TikTok-style feed in React Native with Expo.
But for React DOM (the web) —
- Most examples were for mobile native apps.
- Many web versions lacked smooth auto-play, visibility handling, or performance optimizations.
- Others were just CSS tricks without real video lifecycle control.
So, I decided to build something from scratch, tailored for the web:
react-vertical-feed — a clean, optimized, developer-friendly vertical video feed component for React web apps.
✨ Meet react-vertical-feed
react-vertical-feed
is a lightweight React component that solves the key challenges of building a TikTok-style vertical video experience on the web.
It handles:
- Smooth vertical scrolling
- Automatic play and pause based on video visibility
- Lazy loading and resource management
- Cross-browser compatibility
- Performance optimizations for buttery smooth scrolling
🔥 How It Works
1. Video Visibility with Intersection Observer
The component uses the Intersection Observer API to detect which video is visible, and automatically play or pause it.
useEffect(() => {
const observer = new IntersectionObserver(entries => {
entries.forEach(entry => {
const index = parseInt(entry.target.getAttribute('data-index') || '0', 10);
const item = items[index];
const video = entry.target.querySelector('video') as HTMLVideoElement;
if (entry.isIntersecting) {
video?.play().catch(console.error);
onItemVisible?.(item, index);
} else {
video?.pause();
onItemHidden?.(item, index);
}
});
}, { threshold });
const mediaElements = containerRef.current?.querySelectorAll('[data-index]') || [];
mediaElements.forEach(media => observer.observe(media));
return () => observer.disconnect();
}, [items, onItemVisible, onItemHidden, threshold]);
2. Efficient State Management
Instead of heavy Redux or complex context, we use minimal local state:
const [loadingStates, setLoadingStates] = useState<Record<number, boolean>>({});
const [errorStates, setErrorStates] = useState<Record<number, boolean>>({});
3. Performance First
- Memoization with
useCallback
anduseMemo
- Functional state updates to avoid stale closures
- Clean observer setup and teardown
- Lazy rendering: Only load what's necessary
const mediaElements = useMemo(
() => items.map((item, index) => defaultRenderItem(item, index)),
[items, defaultRenderItem]
);
♿️ Accessibility Built-In
The component includes essential accessibility features:
- ARIA roles (
role="feed"
androle="region"
) - Keyboard navigation (arrow keys)
- Screen reader support with proper labels
- Focus management with
tabIndex
<div
role="feed"
aria-label="Vertical video feed"
tabIndex={0}
onKeyDown={handleKeyDown}
// ... other props
>
🧰 Development Setup
I kept the tooling modern and robust:
- TypeScript for safer code
- Rollup to bundle for both ESM and CommonJS
- Jest + Testing Library for component testing
- ESLint + Prettier for clean formatting
- Husky for pre-commit hooks
Example Rollup config:
export default {
input: 'src/index.ts',
output: [
{ file: 'dist/index.js', format: 'cjs', sourcemap: true },
{ file: 'dist/index.esm.js', format: 'esm', sourcemap: true },
],
// more config...
};
📚 What I Learned
- Simplify early — Complex setups kill performance.
- TypeScript is a must — Caught so many edge cases during development.
- Visibility and resource management are critical for smooth feeds.
- Test-driven development is key — especially when dealing with IntersectionObserver behaviors.
📦 How to Use react-vertical-feed
Installation:
npm install react-vertical-feed
# or
yarn add react-vertical-feed
Then use it like this:
import { VerticalFeed } from 'react-vertical-feed';
<VerticalFeed
items={[
{ src: '/videos/video1.mp4', muted: true, controls: false },
{ src: '/videos/video2.mp4', muted: true, controls: false },
// more items...
]}
/>
You can find the full source on GitHub and a demo here.
Contributions and feedback are welcome!
Top comments (2)
Insane work - actually so much stuff goes into making it smooth like that. you think keeping it simple is always the best bet or do bigger projects need more complexity?
I think early on keeping it simple is always the best move. Simplicity = speed + less bugs. You can examine each feature as its own project once it grows in size.