npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

react-vertical-feed

v0.2.2

Published

A small, accessible React component for TikTok-style vertical video feeds with scroll snap and autoplay

Readme

React Vertical Feed

npm version weekly downloads npm bundle size CI Coverage

A React component for a vertical video feed. Each item fills the feed container. The component uses scroll snap and item visibility to control playback.

The package declares no runtime dependencies. It requires React and React DOM as peer dependencies. The package includes TypeScript types, a CommonJS build, and an ES module build.

Table of Contents

Features

  • 🎥 Automatic video playback based on visibility
  • 🎯 Threshold-aware, transition-only visibility callbacks
  • 🧱 Contained vertical overscroll by default
  • ⌨️ Keyboard navigation support (Arrow keys, Space, Home, End)
  • ♿️ ARIA feed and item metadata
  • 📱 Full-screen and embedded feed layouts
  • 🎨 Customizable loading and error states
  • 🔄 Video loop and poster image support
  • ⚡️ Browser-native preload control and stable observers
  • 📦 TypeScript, CommonJS, and ES module support

Demo

Live Demo

Open the browser console to see visibility and end-of-feed events.

Installation

npm install react-vertical-feed
# or
yarn add react-vertical-feed

Usage

import { VerticalFeed, type VideoItem } from 'react-vertical-feed';

const videos: VideoItem[] = [
  {
    id: 'intro',
    src: 'https://interactive-examples.mdn.mozilla.net/media/cc0-videos/flower.mp4',
    loop: true,
  },
  {
    id: 'demo',
    src: 'https://interactive-examples.mdn.mozilla.net/media/cc0-videos/friday.mp4',
    loop: true,
  },
];

export function App() {
  return <VerticalFeed items={videos} style={{ height: '100dvh' }} />;
}

Videos are muted, inline, and set to autoplay by default. These defaults support browser autoplay rules. Set autoPlay: false on an item to turn off autoplay for that item.

Embedded feeds

Each item uses the size of the feed container. Set the container height when you put the feed in a card or modal.

<VerticalFeed items={videos} style={{ height: 480, borderRadius: 16 }} />

The feed sets overscrollBehaviorY to contain by default. This setting prevents vertical scroll chaining when the feed reaches its first or last item. The browser page does not continue the same wheel or touch scroll.

Set overscrollBehaviorY to auto when you want the browser page to continue scrolling at a feed boundary:

<VerticalFeed items={videos} style={{ height: 480, overscrollBehaviorY: 'auto' }} />

The style prop overrides the default component styles.

Interactive overlays

Use renderItemOverlay for captions and controls. Stop click propagation on each interactive control when the feed has an onItemClick handler.

<VerticalFeed
  items={videos}
  onItemClick={(item, index) => console.log('Selected', item.id, index)}
  renderItemOverlay={(item, index) => (
    <button
      type="button"
      onClick={event => {
        event.stopPropagation();
        console.log('Liked', item.id, index);
      }}
      style={{ position: 'absolute', right: 24, bottom: 24 }}
    >
      Like
    </button>
  )}
/>

Next.js App Router

VerticalFeed uses browser APIs, so render it from a Client Component:

'use client';

import { VerticalFeed } from 'react-vertical-feed';

const videos = [
  {
    id: 'intro',
    src: 'https://interactive-examples.mdn.mozilla.net/media/cc0-videos/flower.mp4',
  },
];

export default function VideoFeed() {
  return <VerticalFeed items={videos} />;
}

API Reference

Props

| Prop | Type | Default | Description | | --------------------- | -------------------------------------------------------- | ------------ | --------------------------------------------- | | items | VideoItem[] | required | Array of video items | | onEndReached | () => void | - | Callback when user scrolls to the end | | loadingComponent | React.ReactNode | - | Custom loading component | | errorComponent | React.ReactNode | - | Custom error component | | className | string | - | Additional CSS class | | style | React.CSSProperties | - | Styles that override the default feed styles | | onItemVisible | (item: VideoItem, index: number) => void | - | Callback when item becomes visible | | onItemHidden | (item: VideoItem, index: number) => void | - | Callback when item becomes hidden | | onItemClick | (item: VideoItem, index: number) => void | - | Callback when item is clicked | | threshold | number | 0.75 | Intersection observer threshold | | scrollBehavior | ScrollBehavior | 'smooth' | Scroll behavior for keyboard navigation | | renderItemOverlay | (item: VideoItem, index: number) => React.ReactNode | - | Custom overlay component for each item | | endReachedThreshold | number | 100 | Distance from bottom to trigger onEndReached | | onVideoError | (item: VideoItem, index: number, error: Error) => void | - | Callback when video loading or playback fails | | onCurrentItemChange | (index: number) => void | - | Callback when current visible item changes | | defaultPreload | 'none' \| 'metadata' \| 'auto' | 'metadata' | Default preload strategy for videos | | getItemKey | (item: VideoItem, index: number) => React.Key | id/index | Stable key for reordered or prepended items |

Types

interface VideoItem {
  src: string;
  id?: string;
  metadata?: Record<string, unknown>;
  controls?: boolean;
  autoPlay?: boolean;
  muted?: boolean;
  playsInline?: boolean;
  loop?: boolean;
  poster?: string;
  preload?: 'none' | 'metadata' | 'auto';
}

interface VerticalFeedRef {
  scrollToItem: (index: number, behavior?: ScrollBehavior) => void;
  getCurrentItem: () => number;
}

Give each dynamic item an id. You can also provide getItemKey. A stable key keeps loading and error state with the correct item after a reorder or prepend operation.

Keyboard Navigation

| Key | Action | | ------- | ------------------------ | | | Scroll to previous video | | | Scroll to next video | | Space | Play/pause current video | | Home | Scroll to first video | | End | Scroll to last video |

Move keyboard focus to the feed before you use these keys. The feed prevents the browser page from performing the same keyboard scroll.

An interactive overlay control owns its keyboard input. The feed does not handle a navigation key when the focused element is a button, link, form control, editable element, or supported interactive ARIA role.

When you provide onItemClick, each item can receive keyboard focus. Press Enter to activate the focused item.

Reduced Motion

The package does not read the prefers-reduced-motion media query. Your application controls autoplay and animation behavior.

The demo reads this media query. When the user requests reduced motion, the demo turns off video autoplay and pauses all videos. The demo also removes the pause-indicator transition and its decorative marquee, like, and loading animations. The user can still start a video with the Play control or a tap.

Programmatic Control

Use a ref to control the feed:

import { useRef } from 'react';
import { VerticalFeed, VerticalFeedRef } from 'react-vertical-feed';

const App = () => {
  const feedRef = useRef<VerticalFeedRef>(null);

  const handleNext = () => {
    const current = feedRef.current?.getCurrentItem() ?? 0;
    feedRef.current?.scrollToItem(current + 1);
  };

  const handlePrev = () => {
    const current = feedRef.current?.getCurrentItem() ?? 0;
    feedRef.current?.scrollToItem(current - 1);
  };

  return (
    <>
      <VerticalFeed ref={feedRef} items={videos} />
      <button onClick={handlePrev}>Previous</button>
      <button onClick={handleNext}>Next</button>
    </>
  );
};

Browser Compatibility

This package ships ES2020 JavaScript for current evergreen browsers. If your application supports an older browser, transpile this package for your browser targets.

The runtime also requires:

  • Intersection Observer API
  • CSS Scroll Snap
  • HTML5 Video

Performance

The component uses these performance controls:

  • Browser-native video preload controls
  • Automatic observer cleanup
  • One container-rooted Intersection Observer
  • Transition-only visibility callbacks

Development

# Install the locked dependencies
npm ci

# Run development server
npm run dev

# Build for production
npm run build

# Lint code
npm run lint

# Format code
npm run format

# Run tests
npm test

# Run tests with coverage
npm run test:coverage

Run the complete package and demo checks from the repository root:

# Verify the package tests, lint, build, size, package contents, and audit
npm run test:release

# Install the locked demo dependencies
npm --prefix examples/demo ci

# Install the Chromium test browser
npm --prefix examples/demo exec -- playwright install chromium

# Run the demo UI, media, and browser tests
npm --prefix examples/demo test

# Type-check and build the demo
npm --prefix examples/demo run build

Test Coverage

The Coverage workflow creates a coverage report for each pull request. To read the report:

  1. Open the latest Coverage workflow run.
  2. Download the coverage-report artifact.
  3. Open index.html in a browser.

License

MIT © reinaldosimoes