Pagination is an essential feature in any application that handles large datasets. It improves performance, enhances user experience, and ensures smooth navigation across pages. This guide provides a step-by-step implementation of pagination for a React frontend (with Tailwind CSS and Vite) and a Node.js/Express.js backend with Next.js API routes and MongoDB.
Why Pagination is Important?
- Performance Optimization: Reduces data load on both the client and server.
- Improved User Experience: Allows users to view data in manageable chunks.
- Scalability: Makes the app efficient even when datasets grow over time.
- Better Resource Management: Reduces memory usage on the frontend and backend.
Tech Stack Overview
- Frontend: React.js, Tailwind CSS, Vite (for a fast development experience).
- Backend: Node.js, Express.js, Next.js API routes.
- Database: MongoDB (NoSQL database).
- Tools: Axios for HTTP requests.
Step-by-Step Implementation
Backend: Building Pagination API with Node.js and MongoDB
1. Set Up Express.js Server
Ensure you have Node.js installed, then set up a new Express.js server.
mkdir pagination-backend
cd pagination-backend
npm init -y
npm install express mongoose dotenv
2. Create Server and MongoDB Connection
Set up a simple server with MongoDB.
server.js
const express = require('express');
const mongoose = require('mongoose');
require('dotenv').config();
const app = express();
const PORT = process.env.PORT || 5000;
// MongoDB Connection
mongoose.connect(process.env.MONGO_URI)
.then(() => console.log('MongoDB connected'))
.catch(err => console.error(err));
app.use(express.json());
app.listen(PORT, () => {
console.log(`Server running on port ${PORT}`);
});
Add your MongoDB URI in a .env
file:
MONGO_URI=mongodb://localhost:27017/pagination_demo
3. Create a Sample Model
Define a sample data schema for storing records.
models/User.js
const mongoose = require('mongoose');
const UserSchema = new mongoose.Schema({
name: String,
email: String,
createdAt: { type: Date, default: Date.now }
});
module.exports = mongoose.model('User', UserSchema);
4. Create Pagination Route
Add a route to return paginated results.
routes/users.js
const express = require('express');
const router = express.Router();
const User = require('../models/User');
// GET Paginated Data
router.get('/users', async (req, res) => {
const page = parseInt(req.query.page) || 1; // Default to page 1
const limit = parseInt(req.query.limit) || 10; // Default 10 items per page
try {
const startIndex = (page - 1) * limit;
const total = await User.countDocuments();
const users = await User.find().skip(startIndex).limit(limit);
res.status(200).json({
totalPages: Math.ceil(total / limit),
currentPage: page,
data: users,
});
} catch (error) {
res.status(500).json({ message: error.message });
}
});
module.exports = router;
5. Add the Route to Server
In server.js
, include the route:
const usersRoute = require('./routes/users');
app.use('/api', usersRoute);
6. Test the API
Run the server:
node server.js
Test with tools like Postman or the browser:
GET http://localhost:5000/api/users?page=1&limit=10
⚛️ Frontend: Implement Pagination with React, Tailwind CSS, and Vite
1. Set Up React with Vite
npm create vite@latest frontend
cd frontend
npm install
npm install axios react-router-dom
2. Fetch Paginated Data
Create a React component to fetch paginated data from the backend API.
components/UserList.jsx
import React, { useState, useEffect } from 'react';
import axios from 'axios';
const UserList = () => {
const [users, setUsers] = useState([]);
const [currentPage, setCurrentPage] = useState(1);
const [totalPages, setTotalPages] = useState(1);
const fetchUsers = async (page) => {
const { data } = await axios.get(
`http://localhost:5000/api/users?page=${page}&limit=10`
);
setUsers(data.data);
setTotalPages(data.totalPages);
setCurrentPage(page);
};
useEffect(() => {
fetchUsers(1);
}, []);
const handlePageChange = (page) => fetchUsers(page);
return (
<div className="p-5">
<h1 className="text-2xl font-bold mb-4">Paginated User List</h1>
<ul>
{users.map((user) => (
<li key={user._id} className="border p-2 mb-2">
{user.name} - {user.email}
</li>
))}
</ul>
{/* Pagination Buttons */}
<div className="flex gap-2 mt-4">
{[...Array(totalPages).keys()].map((_, index) => (
<button
key={index}
onClick={() => handlePageChange(index + 1)}
className={`px-4 py-2 border rounded ${
currentPage === index + 1 ? 'bg-blue-500 text-white' : ''
}`}
>
{index + 1}
</button>
))}
</div>
</div>
);
};
export default UserList;
3. Add Tailwind CSS
Install Tailwind CSS:
npm install -D tailwindcss postcss autoprefixer
npx tailwindcss init -p
Configure tailwind.config.js
:
module.exports = {
content: ["./index.html", "./src/**/*.{js,jsx}",],
theme: {
extend: {},
},
plugins: [],
};
Add Tailwind directives to index.css
:
@tailwind base;
@tailwind components;
@tailwind utilities;
4. Use the UserList Component
Integrate the component in App.jsx
:
import React from 'react';
import UserList from './components/UserList';
function App() {
return (
<div>
<UserList />
</div>
);
}
export default App;
Example Output
The app fetches paginated data from the server and displays it with buttons to navigate pages.
Key Takeaways
- The backend calculates
skip
andlimit
to fetch specific records. - The frontend uses state to manage pagination UI and fetches new data when pages change.
- Tailwind CSS makes styling pagination buttons fast and clean.
Final Notes
This implementation gives you a clean and scalable way to handle pagination for large datasets in a MERN stack application.
Happy coding!
Top comments (0)