Technology

React Items: Infinite Scroll & Virtualization Tutorial

A comprehensive guide to handling large datasets in React applications. Learn how to implement smooth infinite scroll functionality and lightning-fast virtualization using Next.js, TypeScript, and The Movie Database API.

25 min read
Published

Infinite Scroll Tutorial Code

Complete source code for the infinite scroll tutorial with TMDB API integration and responsive design.

View on GitHub

Virtualization Tutorial Code

Complete source code for the React Window virtualization tutorial with five different examples.

View on GitHub

Introduction

Modern web applications frequently need to display large datasets — think social media feeds, product catalogs, or data-heavy dashboards. Two complementary patterns address this challenge: infinite scroll, which loads content progressively as users scroll, and virtualization, which keeps DOM node count constant by only rendering what is visible in the viewport.

This guide consolidates two hands-on tutorials into one comprehensive reference. Part 1 walks through building infinite scroll with Next.js and The Movie Database (TMDB) API — including a bonus virtualized variant. Part 2 dives deep into five distinct virtualization patterns using react-window, from simple fixed-height lists to responsive grid layouts that adapt to any screen size.

Each section is drawn directly from the README of its respective GitHub repository, so you can follow along with the source code as you read.

Part 1: Building Infinite Scroll with Next.js and TMDB API

A Next.js application demonstrating infinite scroll functionality using The Movie Database (TMDB) API. Based on this tutorial.

Features

Infinite Scroll

Automatically loads more movies as you scroll down.

Movie Cards

Displays popular movies with posters, titles, descriptions, and ratings.

Loading States

Shows skeleton loading cards while fetching data.

Error Handling

Graceful error handling with user-friendly messages.

Responsive Design

Mobile-first responsive grid layout (1–4 columns).

Debounced Scrolling

Optimized scroll event handling to prevent excessive API calls. Also includes virtualization for improved performance.

Tech Stack

Next.js 16: React framework with App Router
TypeScript: Type safety
Tailwind CSS: Styling and responsive design
Axios: HTTP client for API requests
Lodash: Utility functions (debounce)
react-window: Virtualized list rendering
TMDB API: The Movie Database API for movie data

Project Structure

src/app/
├── page.tsx                        # Home page with links to demos
├── layout.tsx                      # Root layout
├── globals.css                     # Global styles
├── basic-infinite-scroll/
│   └── page.tsx                    # Basic infinite scroll implementation
├── with-virtualization/
│   └── page.tsx                    # Virtualized infinite scroll implementation
└── components/
    ├── MovieCard.tsx               # Individual movie card component
    └── MovieCardSkeleton.tsx       # Loading skeleton component

Implementations

1. Basic Infinite Scroll (/basic-infinite-scroll)

The classic approach: renders all loaded items into the DOM and listens to the window scroll event.

How it works:

  1. State Management: Uses React hooks to manage page, data, loading, and error state.
  2. Scroll Detection: Triggers when user scrolls within 300px of the bottom:
const handleScroll = () => {
  if (
    document.body.scrollHeight - 300 <
    window.scrollY + window.innerHeight
  ) {
    setPage((prevPage) => prevPage + 1);
  }
};
  1. Debounced Events: Uses lodash debounce to limit scroll event frequency:
const debouncedHandleScroll = debounce(handleScroll, 500);
  1. Data Fetching: Automatically fetches new data when page state changes:
useEffect(() => {
  fetchMovie();
}, [page]);

Trade-off: As more pages are loaded, all movie cards remain mounted in the DOM, which can degrade performance over time.

2. With Virtualization (/with-virtualization)

Uses react-window to render only the rows currently visible in the viewport, keeping DOM node count constant regardless of how many movies have been loaded.

How it works:

  1. Responsive Grid Rows: Movies are grouped into rows based on the current column count (responsive breakpoints matching Tailwind's sm/lg/xl). Each row is a MovieRow component rendered inside the virtualized List.
  2. Dynamic Row Heights: Uses useDynamicRowHeight to measure actual rendered row heights. The key: columnCount prop resets measurements when the column layout changes.
const rowHeight = useDynamicRowHeight({
  defaultRowHeight: 420,
  key: columnCount,
});
  1. Infinite Scroll via onRowsRendered: Instead of listening to window scroll events, the onRowsRendered callback fires whenever the visible row range changes. When the last visible row is within 2 rows of the total row count, the next page is fetched.
const handleRowsRendered = useCallback(
  (visibleRows: { startIndex: number; stopIndex: number }) => {
    if (!hasMore || loading) return;
    if (visibleRows.stopIndex >= rowCount - 2) {
      setPage((prev) => prev + 1);
    }
  },
  [hasMore, loading, rowCount]
);
  1. Duplicate Fetch Prevention: An isFetchingRef ref guard prevents concurrent fetches when state updates trigger multiple renders.

Trade-off: Only visible rows are mounted in the DOM at any time, making this approach much more performant for large datasets.

API Integration

Uses TMDB's popular movies endpoint:

https://api.themoviedb.org/3/movie/popular?language=en-US&page=${page}

Authentication via Bearer token in request headers.

Setup

  1. 1
    Clone the repository:
    git clone https://github.com/audoir/infinite-scroll-tutorial.git
    cd infinite-scroll-tutorial
  2. 2
    Install dependencies:
    npm install
  3. 3
    Environment Setup:

    Create a .env.local file and add your TMDB API key:

    NEXT_PUBLIC_TMDB_READ_ACCESS_TOKEN=your_tmdb_read_access_token_here

    Get your API key from The Movie Database (TMDB).

  4. 4
    Run the development server:
    npm run dev

    Open http://localhost:3000 in your browser.

Learning Outcomes

This project demonstrates key concepts:

  • • React hooks (useState, useEffect, useCallback, useRef)
  • • Event handling and cleanup
  • • API integration with error handling
  • • Performance optimization (debouncing, virtualization)
  • • Responsive design patterns
  • • TypeScript in React applications
  • • List virtualization with react-window

Part 2: Mastering React Window Virtualization

A comprehensive tutorial demonstrating different virtualization techniques using react-window in a Next.js application. This project showcases how to efficiently render large datasets by only displaying visible items in the viewport.

Features

This tutorial includes five different virtualization examples:

1. Fixed Height Lists

  • Demonstrates basic list virtualization with fixed row heights (25px)
  • Includes scroll-to-row functionality with programmatic navigation
  • Uses 448 sample names for demonstration
  • Perfect for simple lists where all items have the same height

2. Variable Height Lists

  • Shows how to handle lists with different row heights
  • States display at 30px height, cities at 25px height
  • Demonstrates hierarchical data visualization (US states and cities)
  • Uses 93 location items with mixed content types

3. Dynamic Height Lists

  • Advanced example with dynamically measured row heights
  • Features collapsible/expandable content with toggle functionality
  • Heights are measured automatically as content changes
  • Uses Lorem ipsum text of varying lengths (21 items)
  • Demonstrates real-world scenarios like comment threads or expandable cards

4. Grid Virtualization

  • 2D virtualization for tabular data
  • Displays contact information in a spreadsheet-like grid
  • Variable column widths optimized for different data types
  • Handles 50 contact records with 10 columns each
  • Efficient for large datasets like data tables or spreadsheets

5. Responsive Grid Virtualization

  • Combines list virtualization with a responsive CSS grid layout
  • Each virtualized row renders a grid of contact cards using Tailwind breakpoints
  • Column count adapts automatically to window width: 1 → 2 → 3 → 4 columns
  • Row heights are measured dynamically with useDynamicRowHeight, resetting when the column count changes
  • Demonstrates how to combine react-window with Tailwind's responsive design system

Tech Stack

Next.js 16.2.0: React framework with App Router
React 19.2.4: UI library
react-window 2.2.7: Virtualization library
TypeScript: Type safety
Tailwind CSS 4: Styling and responsive design

Project Structure

app/
├── components/
│   ├── FixedHeightExample.tsx      # Fixed height list demo
│   ├── VariableHeightExample.tsx   # Variable height list demo
│   ├── DynamicHeightExample.tsx    # Dynamic height list demo
│   ├── DynamicRowComponent.tsx     # Collapsible row component
│   ├── GridExample.tsx             # Grid virtualization demo
│   └── ResponsiveGridExample.tsx   # Responsive grid virtualization demo
├── hooks/
│   └── useListState.ts             # State management for dynamic lists
├── layout.tsx                      # App layout
└── page.tsx                        # Main page with tab navigation

public/
├── data.ts                         # Sample datasets
└── model.ts                        # TypeScript type definitions

Key Concepts Demonstrated

List Virtualization

  • • Only renders visible items plus a small buffer
  • • Dramatically improves performance for large datasets
  • • Maintains scroll position and user experience

Height Management

  • Fixed: All items same height (simplest, fastest)
  • Variable: Predefined heights based on content type
  • Dynamic: Measured heights that can change at runtime

Grid Virtualization

  • • Virtualizes both rows and columns
  • • Optimizes rendering for large tabular data
  • • Supports variable column widths

Responsive Grid Virtualization

  • • Uses List to virtualize rows, with each row containing a CSS grid of cards
  • • Tailwind responsive classes (grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4) control column count
  • useDynamicRowHeight with a key tied to the column count re-measures row heights on layout changes
  • • Demonstrates how to pair virtualization with responsive design without JavaScript-driven layout logic

Performance Benefits

Virtualization provides significant performance improvements:

Memory Usage

Only DOM nodes for visible items — constant memory regardless of dataset size.

Rendering Speed

Constant time complexity regardless of dataset size. Faster page load times.

Scroll Performance

Smooth scrolling even with thousands of items. Maintains scroll position and user experience.

Initial Load

Faster page load times regardless of total dataset size.

Interactive Features

  • Tab Navigation: Switch between different examples
  • Scroll to Row: Jump to specific items in fixed height lists
  • Expand/Collapse: Toggle content in dynamic height lists
  • Responsive Grid: Automatically adjusts columns (1–4) based on window width

Getting Started

  1. 1
    Clone the repository:
    git clone https://github.com/audoir/virtualization-tutorial.git
    cd virtualization-tutorial
  2. 2
    Install dependencies:
    npm install
  3. 3
    Start development server:
    npm run dev
  4. 4
    Open your browser:
    Navigate to http://localhost:3000 to view the tutorial.

Suggested Learning Path

  1. Start with the Fixed Height example to understand basic concepts
  2. Explore Variable Height to see how different item sizes work
  3. Try Dynamic Height for advanced use cases with changing content
  4. Check out the Grid example for tabular data virtualization
  5. Explore Responsive Grid to see how virtualization pairs with Tailwind's responsive design

Learning Outcomes

After exploring this tutorial, you'll understand:

  • When to use virtualization — Performance benefits for large datasets
  • Fixed vs Variable Heights — Different approaches for different use cases
  • Dynamic Height Measurement — Handling content that changes size
  • Grid Virtualization — 2D virtualization for tabular data
  • Responsive Grid — Combining virtualization with responsive CSS grid layouts
  • Performance Optimization — Best practices for large list rendering

Conclusion

Infinite scroll and virtualization are two powerful, complementary patterns for handling large datasets in React applications. Infinite scroll provides a seamless content discovery experience by automatically loading more data as users scroll, while virtualization ensures that even massive datasets render instantly by only mounting visible DOM nodes.

Together, these techniques form the foundation for building scalable, user-friendly applications — from social media feeds and product catalogs to data-heavy dashboards and file explorers. The patterns demonstrated in these tutorials can be adapted to virtually any use case and requirement.

Whether you need the simplicity of basic infinite scroll, the performance of virtualized lists, or the flexibility of a responsive virtualized grid, these repositories give you a solid, production-ready starting point. Experiment with the code, combine the patterns, and adapt them to your own projects.

About the Author

Wayne Cheng is the founder and AI app developer at Audoir, LLC. Prior to founding Audoir, he worked as a hardware design engineer for Silicon Valley startups and an audio engineer for creative organizations. He holds an MSEE from UC Davis and a Music Technology degree from Foothill College.

Further Exploration

To continue exploring these implementations, check out the repositories and experiment with advanced features:

For more web development tutorials and AI-powered tools, visit Audoir .