In today’s e-commerce landscape, users are overwhelmed with choices. The ability to quickly and efficiently filter through products is no longer a luxury but a necessity. Imagine a user browsing an online store, searching for a specific type of shoe. Without effective filtering, they’d have to sift through hundreds, perhaps thousands, of irrelevant products. This is where a dynamic product filter app comes in. This tutorial will guide you through building a dynamic product filter application using Next.js, allowing you to empower users with the tools they need to find exactly what they’re looking for, ultimately improving their shopping experience and boosting your website’s engagement.
What We’ll Build
We’ll create a simple yet functional product filter application. The application will:
- Display a list of products.
- Allow users to filter products based on categories (e.g., shoes, shirts, pants).
- Update the product list dynamically as filters are applied or removed, without a full page reload.
Prerequisites
Before we begin, ensure you have the following:
- Node.js and npm (or yarn) installed on your system.
- A basic understanding of HTML, CSS, and JavaScript.
- Familiarity with React is helpful but not strictly required.
Setting Up the Next.js Project
Let’s start by creating a new Next.js project. Open your terminal and run the following command:
npx create-next-app product-filter-app
cd product-filter-app
This will create a new Next.js project named `product-filter-app`. Navigate into the project directory.
Project Structure Overview
Before we dive into the code, let’s take a quick look at the project structure. Next.js projects typically have the following key directories:
pages: This is where you’ll put your pages. Each file in this directory becomes a route in your application. For example,pages/index.jswill be the home page.components: This directory will hold your reusable React components.public: This directory is for static assets like images, fonts, and other files you want to serve directly.styles: Here you will find styles, usually in CSS modules or styled components.
Creating the Product Data
For this tutorial, we’ll create a simple array of product objects. Each product will have properties like `id`, `name`, `category`, and `price`. Create a file named `products.js` inside a `data` directory (create this directory if it doesn’t exist) at the root of your project. Add the following code:
// data/products.js
const products = [
{
id: 1,
name: "Nike Air Max 270",
category: "shoes",
price: 150,
image: "/images/shoe1.jpg", // Replace with actual image paths
},
{
id: 2,
name: "Adidas Ultraboost",
category: "shoes",
price: 180,
image: "/images/shoe2.jpg",
},
{
id: 3,
name: "Classic Cotton T-Shirt",
category: "shirts",
price: 25,
image: "/images/shirt1.jpg",
},
{
id: 4,
name: "Slim Fit Chinos",
category: "pants",
price: 75,
image: "/images/pant1.jpg",
},
{
id: 5,
name: "Nike Air Force 1",
category: "shoes",
price: 100,
image: "/images/shoe3.jpg",
},
{
id: 6,
name: "Graphic Print Hoodie",
category: "shirts",
price: 60,
image: "/images/shirt2.jpg",
},
{
id: 7,
name: "Denim Jeans",
category: "pants",
price: 85,
image: "/images/pant2.jpg",
},
];
export default products;
Remember to replace the placeholder image paths with actual image file paths or URLs. You can also create a basic images directory in public for the images if you do not have a CDN.
Building the Product List Component
Let’s create a component to display the products. Create a file named `ProductList.js` inside the `components` directory. This component will receive the `products` array as a prop and render each product.
// components/ProductList.js
import Image from 'next/image';
function ProductList({ products }) {
return (
<div>
{products.map((product) => (
<div>
<h3>{product.name}</h3>
<p>Category: {product.category}</p>
<p>Price: ${product.price}</p>
</div>
))}
</div>
);
}
export default ProductList;
This code iterates over the `products` array and renders a `div` for each product, displaying its image, name, category, and price. We’re using Next.js’s Image component for optimized image loading. You will also need to add some basic styling by creating a file `ProductList.module.css` inside the `components` directory:
/* components/ProductList.module.css */
.product-list {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
gap: 20px;
}
.product-item {
border: 1px solid #ccc;
padding: 10px;
text-align: center;
}
Import the CSS module in your ProductList component:
// components/ProductList.js
import styles from './ProductList.module.css';
import Image from 'next/image';
function ProductList({ products }) {
return (
<div>
{products.map((product) => (
<div>
<h3>{product.name}</h3>
<p>Category: {product.category}</p>
<p>Price: ${product.price}</p>
</div>
))}
</div>
);
}
export default ProductList;
Creating the Filter Component
Next, we’ll create a component for our filters. This component will allow users to select categories. Create a file named `Filter.js` inside the `components` directory:
// components/Filter.js
function Filter({ categories, selectedCategories, onCategoryChange }) {
return (
<div>
<h3>Filter by Category</h3>
{categories.map((category) => (
<label>
onCategoryChange(e.target.value, e.target.checked)}
/>
{category}
</label>
))}
</div>
);
}
export default Filter;
This component takes three props: `categories`, `selectedCategories`, and `onCategoryChange`. It renders a list of checkboxes, one for each category. The `onCategoryChange` function is called whenever a checkbox is checked or unchecked, passing the category and the checked state.
And, as before, create a css module in the components directory:
/* components/Filter.module.css */
.filter {
margin-bottom: 20px;
}
.filter h3 {
margin-bottom: 10px;
}
.filter label {
display: block;
margin-bottom: 5px;
}
And import it in the Filter component:
// components/Filter.js
import styles from './Filter.module.css';
function Filter({ categories, selectedCategories, onCategoryChange }) {
return (
<div>
<h3>Filter by Category</h3>
{categories.map((category) => (
<label>
onCategoryChange(e.target.value, e.target.checked)}
/>
{category}
</label>
))}
</div>
);
}
export default Filter;
Integrating Components in the Home Page
Now, let’s integrate these components into our home page (`pages/index.js`). This is where we’ll orchestrate the filtering logic.
// pages/index.js
import { useState, useEffect } from 'react';
import productsData from '../data/products';
import ProductList from '../components/ProductList';
import Filter from '../components/Filter';
function HomePage() {
const [products, setProducts] = useState(productsData);
const [selectedCategories, setSelectedCategories] = useState([]);
const [categories, setCategories] = useState([]);
useEffect(() => {
// Extract unique categories from the products data
const uniqueCategories = [...new Set(productsData.map((product) => product.category))];
setCategories(uniqueCategories);
}, []);
const handleCategoryChange = (category, isChecked) => {
if (isChecked) {
setSelectedCategories([...selectedCategories, category]);
} else {
setSelectedCategories(selectedCategories.filter((c) => c !== category));
}
};
useEffect(() => {
// Filter products based on selected categories
let filteredProducts = productsData;
if (selectedCategories.length > 0) {
filteredProducts = productsData.filter((product) =>
selectedCategories.includes(product.category)
);
}
setProducts(filteredProducts);
}, [selectedCategories]);
return (
<div>
<h1>Product Filter</h1>
</div>
);
}
export default HomePage;
Let’s break down this code:
- We import the necessary components and the product data.
- We use the `useState` hook to manage the `products`, `selectedCategories`, and `categories` states.
- The `useEffect` hook is used to extract unique categories from the `productsData` and set the `categories` state. This happens only once, on component mount.
- The `handleCategoryChange` function updates the `selectedCategories` state based on user interactions with the filter checkboxes.
- Another `useEffect` hook filters the `products` based on the `selectedCategories` whenever `selectedCategories` changes.
- The `Filter` component is passed the available categories, the selected categories, and the `handleCategoryChange` function as props.
- The `ProductList` component receives the filtered `products` as a prop.
Running the Application
Now, run your Next.js application using the following command in your terminal:
npm run dev
# or
yarn dev
Open your browser and go to `http://localhost:3000`. You should see the product list and the filter options. Try selecting different categories, and the product list should update dynamically.
Adding Styles (Optional)
To enhance the visual appearance of your application, you can add CSS. You’ve already created CSS Modules for both the `ProductList` and `Filter` components. You can further customize the overall layout and styling in `styles/globals.css`. Here’s a basic example to get you started:
/* styles/globals.css */
body {
font-family: sans-serif;
margin: 20px;
}
h1 {
margin-bottom: 20px;
}
Import the global styles in your `pages/_app.js` file:
// pages/_app.js
import '../styles/globals.css';
function MyApp({ Component, pageProps }) {
return ;
}
export default MyApp;
Common Mistakes and Troubleshooting
Here are some common mistakes and how to fix them:
- Incorrect File Paths: Double-check your file paths, especially when importing components and image files. Typos in paths are a common source of errors.
- Missing Imports: Make sure you’ve imported all the necessary components and data correctly. React components and data must be imported before they can be used.
- State Management Issues: If your filters aren’t working as expected, review how you’re managing the state. Ensure that the state is being updated correctly when the user interacts with the filter. Pay close attention to the dependencies in your `useEffect` hooks.
- CSS Module Conflicts: If styles are not being applied correctly, verify that you have imported the CSS module correctly in your component and that the class names in your JSX match those in your CSS module.
- Image Paths: When using the Next.js Image component, verify that the image paths are relative to the public directory, and that the images are actually in the location specified.
Key Takeaways
You’ve successfully built a dynamic product filter application using Next.js. Here’s what you’ve learned:
- How to set up a Next.js project.
- How to create and use React components.
- How to manage state using the `useState` hook.
- How to use the `useEffect` hook for side effects, such as filtering data.
- How to create and apply CSS modules for styling.
- How to dynamically update the UI based on user interactions.
FAQ
Here are answers to some frequently asked questions:
- Can I use a database instead of a local data file?
Yes, you can easily fetch your product data from a database or an API endpoint. You would replace the import of `productsData` with a data fetching method (e.g., using `fetch` or a library like `axios`) inside a `useEffect` hook. This is especially useful for larger datasets. - How can I add more filter options (e.g., price range)?
You can extend the `Filter` component to include input fields or sliders for price, size, or other relevant attributes. You would need to add state variables to track the selected values for these filters and modify the filtering logic in the `useEffect` hook accordingly. - How can I improve performance?
For a large dataset, consider techniques like server-side rendering (SSR) or static site generation (SSG) to pre-render the product pages. You can also implement pagination to load products in smaller batches. Optimize your images using Next.js’s Image component to improve load times. - How do I deploy this application?
Next.js applications can be deployed to various platforms, such as Vercel, Netlify, or AWS. Vercel is particularly well-suited for Next.js projects and provides a straightforward deployment process. Simply push your code to a Git repository (e.g., GitHub, GitLab) and connect it to Vercel to deploy.
This tutorial has given you a solid foundation for building dynamic filtering features in your Next.js applications. Building this simple application is just the beginning. The concepts you’ve learned here can be extended to more complex scenarios, such as e-commerce websites, content management systems, or any application where users need to efficiently navigate and find specific data.
