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 (3)
It's about client-side rendering but what about the server-side rendering?
In this modern era, everyone is looking for smooth and simple applications. If your API has multiple models, such as the product, user, order, etc. And suppose you've got a lot of customers in your app then client-side rendering can handle this pressure?
Do you've any ideas or suggestions about server-side paginations? Then I'm looking for that.
I wrote about client site rendering for beginners, will write about server site rendering soon
This absolutely needed for performance improbation and enhance user experience.