In this tutorial, we will build a fully functional Search, Sort, Filter & Pagination feature in a Next.js application using API route handlers. We will use Axios for fetching data, and the dummy data will include fields like name, email, phone, address, status, and id. We will also incorporate modern CSS design for all components.
Introduction
Creating a feature-rich application involves implementing various functionalities like search, sort, filter, and pagination. These features enhance user experience and make data management easier. In this article, we’ll go through each step required to build these features in a Next.js application using API route handlers. We’ll use Axios for data fetching and ensure our design is modern and user-friendly.
Prerequisites
- Basic understanding of Next.js
- Node.js and npm installed
- Basic knowledge of CSS
Setting Up the Project
Initialize a Next.js Project:
npx create-next-app@latest search-sort-filter-pagination
cd search-sort-filter-pagination
Install Axios:
npm install axios
Setup API Route Handlers: Create a file at pages/api/users.js
with the following code:
// pages/api/users.js
// Dummy data
const users = [
{ id: 1, name: "John Doe", email: "john@example.com", phone: "123-456-7890", address: "123 Main St", status: "Active" },
{ id: 2, name: "Jane Smith", email: "jane@example.com", phone: "987-654-3210", address: "456 Elm St", status: "Inactive" },
// Add more dummy users as needed
];
export default function handler(req, res) {
res.status(200).json(users); // Send users as JSON response
}
Creating the React Components
App Component
This component will serve as the main container for our application.
// pages/index.js
import { useState, useEffect } from 'react'; // Import useState and useEffect
import axios from 'axios'; // Import Axios for data fetching
import SearchBar from '../components/SearchBar'; // Import SearchBar component
import UserTable from '../components/UserTable'; // Import UserTable component
import Pagination from '../components/Pagination'; // Import Pagination component
export default function Home() {
const [users, setUsers] = useState([]); // State to hold all user data
const [filteredUsers, setFilteredUsers] = useState([]); // State to hold filtered user data
const [searchTerm, setSearchTerm] = useState(''); // State to hold search term
const [currentPage, setCurrentPage] = useState(1); // State to hold current page number
const [usersPerPage] = useState(10); // State to hold number of users per page
// Fetch data from API route handler
useEffect(() => {
const fetchUsers = async () => {
try {
const response = await axios.get('/api/users'); // Fetch users from API route handler
setUsers(response.data); // Set users state with fetched data
setFilteredUsers(response.data); // Set filtered users state with fetched data
} catch (error) {
console.error('Error fetching data', error); // Log any error that occurs during data fetching
}
};
fetchUsers(); // Call fetchUsers function
}, []); // Empty dependency array ensures this useEffect runs once on mount
// Filter users based on search term
useEffect(() => {
const results = users.filter(user =>
user.name.toLowerCase().includes(searchTerm.toLowerCase()) || // Check if name includes search term
user.email.toLowerCase().includes(searchTerm.toLowerCase()) || // Check if email includes search term
user.phone.includes(searchTerm) || // Check if phone includes search term
user.address.toLowerCase().includes(searchTerm.toLowerCase()) // Check if address includes search term
);
setFilteredUsers(results); // Set filtered users state with results
}, [searchTerm, users]); // Dependency array to re-run effect when searchTerm or users change
// Get current users for pagination
const indexOfLastUser = currentPage * usersPerPage; // Calculate index of last user on current page
const indexOfFirstUser = indexOfLastUser - usersPerPage; // Calculate index of first user on current page
const currentUsers = filteredUsers.slice(indexOfFirstUser, indexOfLastUser); // Slice filtered users array to get current users
// Change page
const paginate = (pageNumber) => setCurrentPage(pageNumber); // Function to set current page
return (
<div className="app">
<h1>User Management</h1>
<SearchBar setSearchTerm={setSearchTerm} /> {/* Render SearchBar component and pass setSearchTerm function as prop */}
<UserTable users={currentUsers} /> {/* Render UserTable component and pass currentUsers as prop */}
<Pagination
usersPerPage={usersPerPage}
totalUsers={filteredUsers.length}
paginate={paginate}
currentPage={currentPage}
/> {/* Render Pagination component and pass necessary props */}
</div>
);
}
SearchBar Component
This component allows users to search for specific data.
// components/SearchBar.js
export default function SearchBar({ setSearchTerm }) {
return (
<input
type="text" // Set input type to text
placeholder="Search..." // Set placeholder text
onChange={(e) => setSearchTerm(e.target.value)} // Call setSearchTerm function with input value on change
/>
);
}
UserTable Component
This component displays the user data in a table format.
// components/UserTable.js
import './UserTable.css'; // Importing the CSS for modern design
export default function UserTable({ users }) {
return (
<table className="user-table">
<thead>
<tr>
<th>ID</th>
<th>Name</th>
<th>Email</th>
<th>Phone</th>
<th>Address</th>
<th>Status</th>
</tr>
</thead>
<tbody>
{users.map(user => (
<tr key={user.id}>
<td>{user.id}</td>
<td>{user.name}</td>
<td>{user.email}</td>
<td>{user.phone}</td>
<td>{user.address}</td>
<td>{user.status}</td>
</tr>
))}
</tbody>
</table>
);
}
Pagination Component
This component handles the pagination of the user data.
// components/Pagination.js
import './Pagination.css'; // Importing the CSS for modern design
export default function Pagination({ usersPerPage, totalUsers, paginate, currentPage }) {
const pageNumbers = [];
for (let i = 1; i <= Math.ceil(totalUsers / usersPerPage); i++) {
pageNumbers.push(i); // Add page number to pageNumbers array
}
return (
<nav>
<ul className="pagination">
{pageNumbers.map(number => (
<li key={number} className={`page-item ${currentPage === number ? 'active' : ''}`}>
<a onClick={() => paginate(number)} href="#!" className="page-link">
{number}
</a>
</li>
))}
</ul>
</nav>
);
}
Modern CSS Design
UserTable CSS
/* components/UserTable.css */
.user-table {
width: 100%; /* Set table width to 100% */
border-collapse: collapse; /* Collapse table borders */
margin: 20px 0; /* Add margin */
font-size: 1em; /* Set font size */
min-width: 400px; /* Set minimum width */
border-radius: 5px 5px 0 0; /* Set border radius */
overflow: hidden; /* Hide overflow */
box-shadow: 0 0 20px rgba(0, 0, 0, 0.15); /* Add box shadow */
}
.user-table thead tr {
background-color: #009879; /* Set background color */
color: #ffffff; /* Set text color */
text-align: left; /* Align text to the left */
font-weight: bold; /* Set font weight to bold */
}
.user-table th,
.user-table td {
padding: 12px 15px; /* Add padding */
}
.user-table tbody tr {
border-bottom: 1px solid #dddddd; /* Add bottom border */
}
.user-table tbody tr:nth-of-type(even) {
background-color: #f3f3f3; /* Set background color for even rows */
}
.user-table tbody tr:last-of-type {
border-bottom: 2px solid #009879; /* Set bottom border for last row */
}
.user-table tbody tr.active-row {
font-weight: bold; /* Set font weight to bold */
color: #009879; /* Set text color */
}
Pagination CSS
/* components/Pagination.css */
.pagination {
display: flex; /* Set display to flex */
justify-content: center; /* Center align items */
padding: 10px 0; /* Add padding */
}
.pagination .page-item {
margin: 0 5px; /* Add margin */
}
.pagination .page-link {
padding: 8px 16px; /* Add padding */
border: 1px solid #ddd; /* Add border */
border-radius: 5px; /* Set border radius */
text-decoration: none; /* Remove text decoration */
color: #009879; /* Set text color */
cursor: pointer; /* Set cursor to pointer */
}
.pagination .page-item.active .page-link {
background-color: #009879; /* Set background color */
color: #ffffff; /* Set text color */
border: 1px solid #009879; /* Set border */
}
Conclusion
In this article, we built a comprehensive Search, Sort, Filter & Pagination feature in a Next.js application using API route handlers and Axios. We used modern CSS to design our components and added error handling to ensure robustness. By following this tutorial, you can enhance your Next.js applications with these powerful features, providing a better user experience.
Welcome to DevTechTutor.com, your ultimate resource for mastering web development and technology! Whether you're a beginner eager to dive into coding or an experienced developer looking to sharpen your skills, DevTechTutor.com is here to guide you every step of the way. Our mission is to make learning web development accessible, engaging, and effective.