DEV Community

Cover image for Day 14 of #100DaysOfCode — Pagination + Infinite Scroll
M Saad Ahmad
M Saad Ahmad

Posted on

Day 14 of #100DaysOfCode — Pagination + Infinite Scroll

Today was Day 14 of my 100 Days of Code, and the topic I focused on was Pagination + Infinite Scroll.
Both solve the exact same problem:
Loading large datasets in smaller chunks instead of all at once.
But the way they trigger new data is what makes them different.


What Is Pagination?

Pagination simply means splitting a large dataset into smaller chunks (pages) instead of showing everything at once.

It’s mostly just math + array.slice(startIndex, endIndex).

✔ Why Pagination Matters

  • Performance — Showing 10 items is faster than rendering 1000
  • User experience — Users don’t want to scroll endlessly
  • Reduced memory usage — The browser handles fewer DOM nodes
  • Control — Users can jump back and forth predictably

✔ When to Use Pagination

Use it when your users need control, structure, or reference points, such as:

  • Search results
  • Product listings
  • Blog archives
  • Tables and dashboards
  • API data that is already paginated

Simple React Pagination Example

import { useState } from 'react';

function PaginatedList() {
  const [currentPage, setCurrentPage] = useState(1);
  const itemsPerPage = 5;

  const allItems = Array.from({ length: 15 }, (_, i) => `Item ${i + 1}`);

  const indexOfLastItem = currentPage * itemsPerPage;
  const indexOfFirstItem = indexOfLastItem - itemsPerPage;
  const currentItems = allItems.slice(indexOfFirstItem, indexOfLastItem);

  const totalPages = Math.ceil(allItems.length / itemsPerPage);

  return (
    <div>
      <ul>
        {currentItems.map((item, index) => (
          <li key={index}>{item}</li>
        ))}
      </ul>

      <button 
        onClick={() => setCurrentPage(currentPage - 1)}
        disabled={currentPage === 1}
      >
        Previous
      </button>

      <span> Page {currentPage} of {totalPages} </span>

      <button 
        onClick={() => setCurrentPage(currentPage + 1)}
        disabled={currentPage === totalPages}
      >
        Next
      </button>
    </div>
  );
}
Enter fullscreen mode Exit fullscreen mode

What Is Infinite Scroll?

Infinite Scroll is a UX pattern where more items automatically load when the user reaches the bottom of the page.

Unlike pagination, the user never presses “Next” — scrolling is the trigger.

✔ Why Infinite Scroll Is Used

  • Smooth and continuous browsing
  • No interactive friction (no clicking)
  • Feels natural on mobile
  • Increases user engagement

✔ Where It Is Usually Used

  • Social media feeds (Twitter, Instagram, Facebook, Pinterest, TikTok)
  • News feeds
  • Image galleries
  • Content discovery platforms

Simple Infinite Scroll Example in React

import { useState, useEffect } from 'react';

function InfiniteScrollList() {
  const [items, setItems] = useState([]);
  const [page, setPage] = useState(1);

  useEffect(() => {
    fetchItems(page);
  }, [page]);

  const fetchItems = (pageNum) => {
    const newItems = Array.from(
      { length: 20 },
      (_, i) => `Item ${(pageNum - 1) * 20 + i + 1}`
    );
    setItems(prev => [...prev, ...newItems]);
  };

  useEffect(() => {
    const handleScroll = () => {
      if (window.innerHeight + window.scrollY >= document.body.offsetHeight - 100) {
        setPage(prev => prev + 1);
      }
    };

    window.addEventListener('scroll', handleScroll);
    return () => window.removeEventListener('scroll', handleScroll);
  }, []);

  return (
    <div>
      {items.map((item, index) => (
        <div key={index} style={{ padding: '20px', border: '1px solid #ccc' }}>
          {item}
        </div>
      ))}
    </div>
  );
}
Enter fullscreen mode Exit fullscreen mode

Pagination vs Infinite Scroll: Are They the Same?

Both solve the same technical problem:

Load large datasets in chunks instead of all at once.

The only real difference is how the next chunk is triggered:

Pattern Trigger
Pagination User clicks “Next/Previous”
Infinite Scroll User scrolls to bottom

But this tiny difference massively impacts UX.


When to Use Which?

Use Infinite Scroll When:

  • The user is browsing casually
  • The content is chronological or endless (feeds)
  • The product wants high engagement
  • Mobile UX is a priority
  • Users don’t need to return to a specific point

Examples: social feeds on Twitter, TikTok, etc.


Use Pagination When:

  • Users are searching intentionally
  • Users need reference points (“Page 3”)
  • Bookmarks matter
  • Comparing items matters
  • The dataset is structured
  • Page footers are important (Infinite scroll can hide them)

Examples:

  • Search results on Google
  • Product search on Amazon
  • Admin panels and dashboards

Real-World Example: Pagination in E-commerce

Perfect for product searches, where the user wants control.

import { useQuery } from '@tanstack/react-query';
import { useState } from 'react';

function ProductSearch() {
  const [page, setPage] = useState(1);

  const { data, isLoading } = useQuery({
    queryKey: ['products', page],
    queryFn: () => fetch(`/api/products?page=${page}`).then(res => res.json()),
    keepPreviousData: true,
  });

  return (
    <div>
      {isLoading ? (
        <p>Loading...</p>
      ) : (
        <>
          {data.products.map(product => (
            <div key={product.id}>
              {product.name} - ${product.price}
            </div>
          ))}

          <div>
            <button 
              onClick={() => setPage(p => p - 1)} 
              disabled={page === 1}
            >
              Previous
            </button>

            <span>Page {page} of {data.totalPages}</span>

            <button 
              onClick={() => setPage(p => p + 1)} 
              disabled={page === data.totalPages}
            >
              Next
            </button>
          </div>
        </>
      )}
    </div>
  );
}
Enter fullscreen mode Exit fullscreen mode
Concept Explanation
useState(1) Stores the current page number and updates it when the user navigates.
useQuery() Fetches product data from the API based on the current page and caches each result.
queryKey: ['products', page] Ensures React Query refetches data whenever page changes.
queryFn Calls the backend API (/api/products?page=x) and returns JSON data.
keepPreviousData: true Prevents UI flickering by showing previous results until new data finishes loading.
data.products.map() Renders the products for the current page.
Pagination buttons Update the page number and disable themselves when at the first or last page.

This component fetches paginated product data from an API and displays one page at a time. React Query automatically re-requests new data whenever the user clicks “Next” or “Previous” by changing the page state. The UI stays smooth because keepPreviousData prevents the list from briefly disappearing between page transitions. Only the data for the current page is shown, and the buttons dynamically disable when the user reaches the first or last page.

This is classic frontend pagination — the backend returns data for page, and the frontend simply switches pages using state and React Query caching.


Real-World Example: Infinite Scroll in Social Feeds

Feeds on platforms like Instagram and Facebook load naturally as you scroll.

import { useInfiniteQuery } from '@tanstack/react-query';
import { useEffect } from 'react';

function SocialFeed() {
  const {
    data,
    fetchNextPage,
    hasNextPage,
    isFetchingNextPage,
  } = useInfiniteQuery({
    queryKey: ['posts'],
    queryFn: ({ pageParam = 1 }) =>
      fetch(`/api/posts?page=${pageParam}`).then(res => res.json()),
    getNextPageParam: (lastPage) =>
      lastPage.hasMore ? lastPage.nextPage : undefined,
  });

  useEffect(() => {
    const handleScroll = () => {
      const bottom =
        window.innerHeight + window.scrollY >=
        document.body.offsetHeight - 200;

      if (bottom && hasNextPage && !isFetchingNextPage) {
        fetchNextPage();
      }
    };

    window.addEventListener('scroll', handleScroll);
    return () => window.removeEventListener('scroll', handleScroll);
  }, [hasNextPage, isFetchingNextPage, fetchNextPage]);

  return (
    <div>
      {data?.pages.map((page, i) => (
        <div key={i}>
          {page.posts.map(post => (
            <div key={post.id} style={{ padding: '20px', border: '1px solid #ddd' }}>
              <h3>{post.author}</h3>
              <p>{post.content}</p>
            </div>
          ))}
        </div>
      ))}
      {isFetchingNextPage && <p>Loading more posts...</p>}
    </div>
  );
}
Enter fullscreen mode Exit fullscreen mode
Concept Explanation
useInfiniteQuery() Fetches paginated data and automatically manages multiple pages for infinite scrolling.
pageParam Determines which page to load next and defaults to page 1 on first load.
getNextPageParam() Tells React Query which page to fetch next based on the backend’s response.
fetchNextPage() Loads the next page of posts and appends them to the existing list.
Scroll detection (handleScroll) Checks when the user reaches near the bottom of the page.
hasNextPage && !isFetchingNextPage Ensures new pages load only when more data is available and no current fetch is running.
data.pages.map() Loops through all loaded pages and renders posts from each page sequentially.

This component implements infinite scrolling by continuously fetching more posts as the user reaches the bottom of the page. React Query manages pages internally and loads new data using fetchNextPage(), while the useEffect scroll listener detects when the user is near the bottom. Each fetched page gets appended to the existing feed, creating a seamless, continuous scrolling experience. As long as the backend reports hasMore, the feed keeps expanding without any “Next” buttons.

This is true infinite scroll — scroll triggers the fetch, React Query stacks the pages, and the UI grows dynamically.


Quick Cheat Sheet

Use Case Best Choice
Social media feed Infinite scrol
Product search Pagination
Blog index Pagination
Chronological news feed Infinite scroll
Admin dashboard Pagination
Image discovery (like Pinterest) Infinite scroll
Search results (like Google) Pagination

Final Takeaway

Pagination gives control. Infinite scroll gives immersion.
Both load data in chunks, but the trigger mechanism changes everything.

👉 If you want your users to browse endlessly → choose Infinite Scroll.
👉 If you want users to find what they’re looking for → choose Pagination.

Happy coding!

Top comments (0)