Pagination in React with useState

Used in this guide:
Next.js 15.3.4
TypeScript
Tailwind CSS

Introduction

This tutorial walks through how to build a simple pagination system using React’s useState. You’ll display a list of items, navigate between pages, and allow users to jump to a specific page.

This example demonstrates how to implement basic pagination using React's useState. The list contains 24 fruits, and we paginate the display by showing 4 items per page. Users can navigate using "Prev" and "Next" buttons, or jump directly to a specific page using an input field. The pagination logic is fully handled on the client side, without needing external libraries or server-side data.

app/page.tsx
'use client'
import { useState } from "react";
 
const fruits = [
  "Apple",
  "Banana",
  "Orange",
  "Grape",
  "Strawberry",
  "Watermelon",
  "Pineapple",
  "Mango",
  "Kiwi",
  "Cherry",
  "Peach",
  "Pear",
  "Plum",
  "Lemon",
  "Lime",
  "Raspberry",
  "Blueberry",
  "Blackberry",
  "Pomegranate",
  "Fig",
  "Apricot",
  "Cantaloupe",
  "Dragon fruit",
  "Passion fruit"
]
 
export default function Home() {
 
	const [currentPage, setCurrentPage] = useState(1);
	const itemsPerPage = 4;
	const totalPages = Math.ceil(fruits.length / itemsPerPage);
	const startingIndex = (currentPage - 1) * itemsPerPage; // first page starts at index 0, second age starts at index 4 etc...
	const currentShowing = fruits.slice(startingIndex, startingIndex + itemsPerPage); // from starting index + 4 will be the fruits we want to display
 
	const goToNext = () => setCurrentPage(prev => Math.min(prev + 1, totalPages));
	const goToPrev = () => setCurrentPage(prev => Math.max(prev -1, 1));
 
	return (
		<div className="flex flex-col gap-4 p-8">
			<h1 className="font-semibold text-lg">Pagination Tutorial</h1>
			{currentShowing && (
				<div className="flex gap-4">
					{currentShowing.map((fruit) => (
						<span 
							key={fruit}
							className="bg-slate-600 py-2 px-4 rounded-md"
							>{fruit}</span>
					))}
				</div>
			)}
			{fruits.length > 0 && (
				<div className="flex items-center gap-4">
					<button
						onClick={goToPrev}
						disabled={currentPage === 1}
						className="bg-sky-600 text-sm py-2 px-4 cursor-pointer rounded-md disabled:opacity-50"
						>Prev</button>
					<span>Page {currentPage} of {totalPages}</span>
					<button
						onClick={goToNext}
						disabled={currentPage === totalPages}
						className="bg-sky-600 text-sm py-2 px-4 cursor-pointer rounded-md disabled:opacity-50"
						>Next</button>
					<div className="flex gap-2 items-center">
						Go to page: 
						<input 
							type="number" 
							value={currentPage} 
							onChange={e => setCurrentPage(Number(e.target.value))}
							min={1}
							max={totalPages}
							className="bg-slate-400 text-slate-800 p-2 "
							/>
					</div>
				</div>
			)}
		</div>
	);
}
  • We use useState to track the currentPage. The itemsPerPage is fixed at 4. From there, we calculate how many total pages we need.
  • We compute the startingIndex to slice the fruits array and get only the items for the current page.
  • goToNext and goToPrev handle clicking the Next and Prev buttons, while making sure the current page stays within valid limits.

JKT

Stay focused, and the rest will follow

©Jakkrit Turner. All rights reserved