Pagination
Build a pagination component using React with page navigation, data chunking, and user-friendly controls
Pagination - Machine Coding Question
Problem Statement
Create a pagination component that allows users to:
- View data in manageable chunks (pages)
- Navigate between pages using controls
- Display current page status
- Handle large datasets efficiently
- Provide intuitive navigation experience
🧠 Understanding the Problem
Think of pagination like a book with many pages. Instead of showing all the content at once (which would be overwhelming), we show one page at a time and provide navigation controls to move between pages.
Why Do We Need Pagination?
- Performance: Loading 1000 items at once is slow and uses lots of memory
- User Experience: Users can focus on a smaller set of items
- Navigation: Easy to find specific items
- Mobile Friendly: Better for smaller screens
🏗️ System Design & Architecture
Core Concepts
1. Page Size
The number of items shown per page (e.g., 10 items per page)
2. Total Pages
Calculated as: Math.ceil(totalItems / pageSize)
3. Current Page
The page currently being displayed
4. Navigation Controls
- Previous/Next buttons
- Page numbers
- Ellipsis for large page counts
Component Architecture
App
├── ProductList (displays current page items)
└── Pagination
├── Previous Button
├── Page Numbers
└── Next ButtonStep-by-Step Implementation
Step 1: Project Setup
First, let's create our React project:
npx create-react-app pagination-demo
cd pagination-demoStep 2: Understanding the Data Flow
How Pagination Works:
- Fetch Data: Get all items from API
- Calculate Pages: Determine how many pages we need
- Slice Data: Show only items for current page
- Handle Navigation: Update current page when user clicks
Step 3: Building the Main Component
Create src/App.js:
import "./styles.css";
import { useState, useEffect } from "react";
export default function App() {
// State for storing all products
const [products, setProducts] = useState([]);
// State for current page (starts at page 1)
const [page, setPage] = useState(1);
// Fetch products from API
const fetchProducts = async () => {
try {
const res = await fetch("https://dummyjson.com/products?limit=100");
const data = await res.json();
if (data.products && data.products.length) {
setProducts(data.products);
}
} catch (error) {
console.error("Error fetching products:", error);
}
};
// Fetch products when component mounts
useEffect(() => {
fetchProducts();
}, []);
// Handle page change
const handlePageChange = (pageNumber) => {
const totalPages = Math.ceil(products.length / 10);
// Validate page number
if (
pageNumber > 0 &&
pageNumber <= totalPages &&
pageNumber !== page
) {
setPage(pageNumber);
}
};
return (
<div className="App">
<h1>🛍️ All Products</h1>
{/* Display products for current page */}
{products.length > 0 && (
<ol className="All__products">
{products
.slice((page - 1) * 10, page * 10)
.map((product) => (
<li key={product.id} className="product">
<img src={product.thumbnail} alt={product.title} />
<h4>{product.title}</h4>
<p>${product.price}</p>
</li>
))}
</ol>
)}
{/* Pagination controls */}
{products.length > 0 && (
<section className="pagination">
{/* Previous button */}
<span
onClick={() => handlePageChange(page - 1)}
className={`arrow ${page === 1 ? "pagination__disabled" : ""}`}
>
⬅ Previous
</span>
{/* Page numbers */}
{[...Array(Math.ceil(products.length / 10))].map((_, i) => (
<span
className={`page__number ${
page === i + 1 ? "selected__page__number" : ""
}`}
key={i + 1}
onClick={() => handlePageChange(i + 1)}
>
{i + 1}
</span>
))}
{/* Next button */}
<span
onClick={() => handlePageChange(page + 1)}
className={`arrow ${
page === Math.ceil(products.length / 10)
? "pagination__disabled"
: ""
}`}
>
Next ➡
</span>
</section>
)}
</div>
);
}Step 4: Understanding the Key Logic
Data Slicing Formula
// Show items from index (page - 1) * 10 to page * 10
products.slice((page - 1) * 10, page * 10)Example:
- Page 1: Items 0-9 (index 0 to 9)
- Page 2: Items 10-19 (index 10 to 19)
- Page 3: Items 20-29 (index 20 to 29)
Page Number Generation
// Create array with length equal to total pages
[...Array(Math.ceil(products.length / 10))]Example:
- 100 products ÷ 10 per page = 10 pages
- Creates array: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
- Maps to page numbers: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
Step 5: Styling Our Component
Create src/styles.css:
.App {
text-align: center;
padding: 20px;
background-color: #f5f5f5;
min-height: 100vh;
}
h1 {
color: #333;
margin-bottom: 30px;
}
.All__products {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
gap: 20px;
list-style: none;
padding: 0;
margin: 20px 0;
}
.product {
background: white;
border-radius: 8px;
padding: 15px;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
transition: transform 0.2s;
}
.product:hover {
transform: translateY(-2px);
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.15);
}
.product img {
width: 100%;
height: 200px;
object-fit: cover;
border-radius: 4px;
margin-bottom: 10px;
}
.product h4 {
margin: 10px 0;
color: #333;
font-size: 16px;
}
.product p {
color: #666;
font-weight: bold;
margin: 5px 0;
}
/* Pagination Styles */
.pagination {
display: flex;
justify-content: center;
align-items: center;
gap: 10px;
margin-top: 30px;
flex-wrap: wrap;
}
.page__number {
padding: 8px 12px;
border: 1px solid #ddd;
background: white;
cursor: pointer;
border-radius: 4px;
transition: all 0.2s;
min-width: 40px;
text-align: center;
}
.page__number:hover {
background: #f0f0f0;
border-color: #999;
}
.selected__page__number {
background: #007bff;
color: white;
border-color: #007bff;
}
.selected__page__number:hover {
background: #0056b3;
}
.arrow {
padding: 8px 12px;
border: 1px solid #ddd;
background: white;
cursor: pointer;
border-radius: 4px;
transition: all 0.2s;
}
.arrow:hover {
background: #f0f0f0;
border-color: #999;
}
.pagination__disabled {
opacity: 0.5;
cursor: not-allowed;
}
.pagination__disabled:hover {
background: white;
border-color: #ddd;
}Key Features Explained
1. State Management
products: Stores all fetched datapage: Tracks current page number- Both states work together to show correct data
2. Data Fetching
- Uses
useEffectto fetch data on component mount - Handles API errors gracefully
- Stores all data in state for client-side pagination
3. Page Calculation
Math.ceil(products.length / 10): Calculates total pages- Ensures we have enough pages for all items
4. Data Slicing
slice((page - 1) * 10, page * 10): Shows only current page items- Zero-based indexing handled correctly
5. Navigation Logic
- Validates page numbers before changing
- Prevents invalid page navigation
- Handles edge cases (first/last page)
🧩 Understanding the Code Flow
- Component Mounts:
useEffecttriggers data fetch - Data Received: Products stored in state
- Initial Render: Shows page 1 with first 10 items
- User Clicks Page:
handlePageChangevalidates and updates page - Re-render: Component shows new page's items
- Navigation: Previous/Next buttons work with validation
Running the Application
npm startYour pagination app will be running at http://localhost:3000!
What You've Learned
- Data Chunking: How to split large datasets into pages
- State Management: Managing multiple related states
- Array Manipulation: Using slice and map for pagination
- User Interface: Creating intuitive navigation controls
- API Integration: Fetching and handling external data
Advanced Pagination Features
1. Ellipsis for Large Page Counts
const renderPageNumbers = () => {
const totalPages = Math.ceil(products.length / 10);
const pages = [];
if (totalPages <= 7) {
// Show all pages
for (let i = 1; i <= totalPages; i++) {
pages.push(i);
}
} else {
// Show first 3, last 3, and current with ellipsis
pages.push(1, 2, 3, '...', totalPages - 2, totalPages - 1, totalPages);
}
return pages;
};2. Dynamic Page Size
const [pageSize, setPageSize] = useState(10);
// Allow users to change items per page
const handlePageSizeChange = (newSize) => {
setPageSize(newSize);
setPage(1); // Reset to first page
};3. Server-Side Pagination
const fetchProducts = async (pageNumber, pageSize) => {
const res = await fetch(
`https://api.example.com/products?page=${pageNumber}&limit=${pageSize}`
);
const data = await res.json();
return data;
};Tips for Interview
- Start Simple: Begin with basic page navigation
- Add Features Incrementally: Add ellipsis, page size, etc.
- Handle Edge Cases: Empty data, single page, invalid navigation
- Performance: Consider server-side pagination for large datasets
- Accessibility: Add ARIA labels and keyboard navigation
🎉 Conclusion
Congratulations! You've built a functional pagination component that demonstrates:
- Efficient data handling
- User-friendly navigation
- Responsive design
- State management best practices
This pagination pattern is used everywhere:
- E-commerce product listings
- Blog post archives
- Search results
- Social media feeds
- Admin dashboards
The concepts you've learned apply to any scenario where you need to display large amounts of data in manageable chunks. Keep building and exploring!
🔗 Related Concepts
- Infinite Scroll: Alternative to pagination
- Virtual Scrolling: For very large lists
- Lazy Loading: Loading data as needed
- Caching: Storing fetched data
- Search & Filter: Combining with pagination
Frontend Machine Coding: Build a Dynamic Modal Component
Learn how to build a flexible modal component with smooth animations, accessibility features, and configurable behavior using React
Secure Password Generator Component
Master machine coding by creating a secure password generator with customizable options, copy-to-clipboard functionality, and advanced security features.