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>
);
}
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>
);
}
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>
);
}
| 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>
);
}
| 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)