Build a Simple Next.js Interactive Product Filtering App

Written by

in

In today’s fast-paced digital landscape, users expect a seamless and intuitive online shopping experience. One of the most critical elements of a positive e-commerce journey is the ability to easily find what you’re looking for. This is where product filtering comes in. Imagine a user browsing through hundreds of products – without effective filtering options, they’d be lost in a sea of options, likely leading to frustration and abandoned shopping carts. This is why building a robust product filtering system is crucial for any e-commerce application. Next.js, with its powerful features and ease of use, provides an excellent framework for creating such a system.

Why Build a Product Filtering App?

Product filtering empowers users to narrow down their search based on specific criteria, such as price, size, color, brand, and more. This targeted approach allows users to quickly find the products that meet their exact needs, saving them time and improving their overall shopping experience. From a business perspective, effective filtering can lead to increased sales, improved customer satisfaction, and a higher conversion rate.

Consider the following scenarios:

  • A user wants to find blue running shoes within a specific price range.
  • A customer is looking for a laptop with a certain amount of RAM and storage.
  • A shopper wants to see all products from a specific brand.

Without product filtering, these tasks would be incredibly difficult and time-consuming. Building a product filtering app solves these problems, making your e-commerce site user-friendly and efficient.

Prerequisites

Before we dive in, let’s make sure you have the necessary tools and understanding:

  • Node.js and npm (or yarn): You’ll need Node.js installed on your machine, along with npm or yarn for package management.
  • Basic JavaScript and React knowledge: Familiarity with JavaScript and the React library is essential, as Next.js is built on React.
  • A code editor: Choose your preferred code editor (VS Code, Sublime Text, Atom, etc.).
  • A basic understanding of HTML and CSS: While this tutorial focuses on the logic, some familiarity with HTML and CSS will help you customize the appearance of your app.

Step-by-Step Guide to Building the Product Filtering App

Let’s get started! We’ll walk through the process step-by-step, building a functional and user-friendly product filtering application using Next.js.

1. Setting Up Your Next.js Project

First, let’s create a new Next.js project. Open your terminal and run the following command:

npx create-next-app product-filtering-app
cd product-filtering-app

This command creates a new Next.js project named “product-filtering-app”. Navigate into the project directory using `cd product-filtering-app`.

2. Project Structure and Initial Files

Your project directory will contain several files and folders. The most important ones for this project are:

  • `pages/`: This directory holds your Next.js pages. Each file in this directory represents a route in your application. For example, `pages/index.js` corresponds to the `/` route.
  • `components/`: This directory is where you’ll store your reusable React components. This helps keep your code organized and maintainable.
  • `styles/`: This directory will contain your CSS or other styling files.
  • `public/`: This directory is for static assets such as images and fonts.
  • `package.json`: This file contains your project’s metadata and dependencies.

3. Creating the Product Data

For this tutorial, we’ll use a simple array of product objects. Create a file named `data.js` inside the `components` folder. Add the following code:

// components/data.js
const products = [
 {
  id: 1,
  name: "Awesome T-Shirt",
  category: "Clothing",
  color: "Blue",
  size: "Medium",
  price: 25,
  image: "/images/tshirt_blue.jpg", // Replace with your image path
 },
 {
  id: 2,
  name: "Stylish Jeans",
  category: "Clothing",
  color: "Blue",
  size: "32",
  price: 75,
  image: "/images/jeans_blue.jpg", // Replace with your image path
 },
 {
  id: 3,
  name: "Red Running Shoes",
  category: "Shoes",
  color: "Red",
  size: "10",
  price: 100,
  image: "/images/shoes_red.jpg", // Replace with your image path
 },
 {
  id: 4,
  name: "Black Hoodie",
  category: "Clothing",
  color: "Black",
  size: "Large",
  price: 50,
  image: "/images/hoodie_black.jpg", // Replace with your image path
 },
 {
  id: 5,
  name: "White Sneakers",
  category: "Shoes",
  color: "White",
  size: "8",
  price: 80,
  image: "/images/sneakers_white.jpg", // Replace with your image path
 },
 {
  id: 6,
  name: "Green Skirt",
  category: "Clothing",
  color: "Green",
  size: "Small",
  price: 40,
  image: "/images/skirt_green.jpg", // Replace with your image path
 },
 {
  id: 7,
  name: "Brown Boots",
  category: "Shoes",
  color: "Brown",
  size: "9",
  price: 120,
  image: "/images/boots_brown.jpg", // Replace with your image path
 },
 {
  id: 8,
  name: "Yellow Hat",
  category: "Accessories",
  color: "Yellow",
  size: "One Size",
  price: 20,
  image: "/images/hat_yellow.jpg", // Replace with your image path
 },
 {
  id: 9,
  name: "Grey Scarf",
  category: "Accessories",
  color: "Grey",
  size: "One Size",
  price: 30,
  image: "/images/scarf_grey.jpg", // Replace with your image path
 },
 {
  id: 10,
  name: "Black Gloves",
  category: "Accessories",
  color: "Black",
  size: "One Size",
  price: 35,
  image: "/images/gloves_black.jpg", // Replace with your image path
 }
];

export default products;

This `data.js` file exports an array of product objects, each with properties like `id`, `name`, `category`, `color`, `size`, `price`, and `image`. Make sure you replace the image paths (`/images/…`) with your own image paths if you plan on displaying images. You will need to create an `images` folder inside `public` and place your images there, or update the paths accordingly.

4. Creating the Product List Component

Next, let’s create a component to display the products. Create a file named `ProductList.js` inside the `components` folder. This component will take an array of products as a prop and render them.


// 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>Color: {product.color}</p>
     <p>Size: {product.size}</p>
     <p>Price: ${product.price}</p>
    </div>
   ))}
  </div>
 );
}

export default ProductList;

This component iterates over the `products` array and renders a `div` for each product, displaying its details. We use the `Image` component from `next/image` for optimized image loading. We are also using basic HTML elements, such as `h3` and `p`, to display the product information. For styling, we’ll add some CSS later.

5. Creating the Filter Component

Now, let’s create the filter component. This component will handle the filtering logic and display the filter options. Create a file named `Filter.js` inside the `components` folder.


// components/Filter.js
import { useState } from 'react';

function Filter({ products, onFilterChange }) {
 const [filters, setFilters] = useState({});

 const categories = [...new Set(products.map((product) => product.category))];
 const colors = [...new Set(products.map((product) => product.color))];
 const sizes = [...new Set(products.map((product) => product.size))];

 const handleFilterChange = (filterType, value) => {
  setFilters((prevFilters) => ({
   ...prevFilters,
   [filterType]: value,
  }));

  // Apply the filters
  const filteredProducts = products.filter((product) => {
   let isValid = true;
   for (const key in filters) {
    if (filters.hasOwnProperty(key)) {
     if (key === 'price') {
      const [min, max] = filters[key].split('-').map(Number);
      if (product.price  max) {
       isValid = false;
       break;
      }
     } else if (filters[key] && product[key] !== filters[key]) {
      isValid = false;
      break;
     }
    }
   }
   return isValid;
  });

  onFilterChange(filteredProducts);
 };

 return (
  <div>
   <h2>Filter Products</h2>
   <div>
    <h3>Category</h3>
    {categories.map((category) => (
     <label>
       handleFilterChange('category', e.target.value)}
       checked={filters.category === category}
      />
      {category}
     </label>
    ))}
   </div>
   <div>
    <h3>Color</h3>
    {colors.map((color) => (
     <label>
       handleFilterChange('color', e.target.value)}
       checked={filters.color === color}
      />
      {color}
     </label>
    ))}
   </div>
   <div>
    <h3>Size</h3>
    {sizes.map((size) => (
     <label>
       handleFilterChange('size', e.target.value)}
       checked={filters.size === size}
      />
      {size}
     </label>
    ))}
   </div>
   {/* Add Price Filter Here */}
  </div>
 );
}

export default Filter;

This component manages filter states using the `useState` hook. It extracts unique categories, colors, and sizes from the product data. The `handleFilterChange` function updates the filter state and calls the `onFilterChange` prop function to apply the filters. This component will render filter options as radio buttons for category, color, and size. We will add the price filter in the next step.

6. Adding the Price Filter

Let’s add a price filter to the `Filter.js` component. This will allow users to filter products based on a price range. Add the following code inside the `Filter.js` component, just before the closing `

` of the filter container:


   <div>
    <h3>Price</h3>
    <label>
      handleFilterChange('price', e.target.value)}
      checked={filters.price === '0-50'}
     />
     $0 - $50
    </label>
    <label>
      handleFilterChange('price', e.target.value)}
      checked={filters.price === '51-100'}
     />
     $51 - $100
    </label>
    <label>
      handleFilterChange('price', e.target.value)}
      checked={filters.price === '101-'}
     />
     $101+
    </label>
   </div>

This code adds radio buttons for different price ranges. The `handleFilterChange` function will now also handle the price filter. The `handleFilterChange` function needs to be updated to parse the price range correctly. Inside the `handleFilterChange` function, add the following logic inside the `if (key === ‘price’)` condition:


  if (key === 'price') {
   const [min, max] = filters[key].split('-').map(Number);
   if (product.price  max)) {
    isValid = false;
    break;
   }
  }

This code splits the price range string into minimum and maximum values and checks if the product price falls within the selected range. If the max value is missing (e.g., ‘101-‘), then it checks only for a minimum price.

7. Integrating Components in the Main Page

Now, let’s put it all together in the `pages/index.js` file. This is the main page of your application. Replace the contents of `pages/index.js` with the following code:


// pages/index.js
import { useState } from 'react';
import products from '../components/data';
import ProductList from '../components/ProductList';
import Filter from '../components/Filter';
import '../styles/Home.module.css'; // Import the CSS file

export default function Home() {
 const [filteredProducts, setFilteredProducts] = useState(products);

 const handleFilterChange = (filtered) => {
  setFilteredProducts(filtered);
 };

 return (
  <div>
   
   
  </div>
 );
}

This code imports the product data, `ProductList`, and `Filter` components. It uses the `useState` hook to manage the filtered products. The `handleFilterChange` function is passed to the `Filter` component, which updates the `filteredProducts` state based on the selected filters. Finally, it renders the `Filter` and `ProductList` components.

8. Adding Styling (CSS)

To make your app look good, let’s add some CSS. Create a file named `Home.module.css` inside the `styles` folder. Add the following styles:


/* styles/Home.module.css */

.container {
  display: flex;
  padding: 20px;
}

.filter-container {
  width: 200px;
  margin-right: 20px;
  padding: 10px;
  border: 1px solid #ccc;
  border-radius: 5px;
}

.product-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
  gap: 20px;
}

.product-card {
  border: 1px solid #eee;
  padding: 10px;
  border-radius: 5px;
  text-align: center;
}

.product-card img {
  max-width: 100%;
  height: auto;
  margin-bottom: 10px;
}

.filter-container h2, .filter-container h3 {
  margin-bottom: 10px;
}

.filter-container label {
  display: block;
  margin-bottom: 5px;
}

This CSS provides basic styling for the layout, filter container, product cards, and images. You can customize these styles to match your desired design.

9. Running Your Application

Now, it’s time to run your application. In your terminal, make sure you’re in the project directory and run the following command:

npm run dev

or

yarn dev

This will start the development server. Open your web browser and go to `http://localhost:3000`. You should see your product filtering app in action! You should be able to see the products displayed, and filter them by category, color, size, and price.

Common Mistakes and How to Fix Them

Let’s address some common issues you might encounter while building your product filtering app and how to resolve them:

  • Incorrect File Paths: Ensure that your file paths in the `import` statements and image paths in the `data.js` file are correct. Double-check the spelling and casing.
  • CSS Issues: If your styles aren’t applying, make sure you’ve imported the CSS file correctly in `pages/index.js` and that your CSS class names match the ones used in your components. Also, make sure the CSS is valid.
  • Filter Logic Errors: Carefully review your filter logic, especially the `handleFilterChange` function in the `Filter.js` component. Make sure the filter conditions are correctly applied and that the filtered products are being updated properly. Use `console.log` statements to debug the filter values and the filtering process.
  • Image Loading Errors: If images aren’t displaying, verify that the image paths in your `data.js` file are correct, and that the images are located in the `public/images` folder (or the path you specified). Also, ensure that the file extensions are correct (e.g., .jpg, .png).
  • State Management Problems: Make sure you’re correctly updating the state variables using the `useState` hook. Incorrect state updates can lead to unexpected behavior. Use the browser’s developer tools to inspect the component’s state and props.

Key Takeaways and Best Practices

Here are some key takeaways and best practices for building product filtering apps:

  • Component-Based Architecture: Break down your app into reusable components (ProductList, Filter, etc.) to improve code organization and maintainability.
  • State Management: Use the `useState` hook (or a state management library like Redux or Zustand for more complex apps) to manage the state of your filter options and filtered products.
  • Data Fetching: If your product data comes from an API, use `useEffect` (and `fetch` or a library like Axios) to fetch the data when the component mounts.
  • Performance Optimization: Use techniques like memoization and code splitting to optimize your app’s performance, especially for large datasets.
  • User Experience: Provide clear and intuitive filter options. Consider adding a “clear filters” button.
  • Accessibility: Ensure your app is accessible to users with disabilities. Use semantic HTML and ARIA attributes where necessary.
  • Error Handling: Implement error handling to gracefully handle potential issues, such as API errors or invalid data.

SEO Best Practices

Optimizing your product filtering app for search engines (SEO) is essential for attracting organic traffic. Here are some SEO best practices:

  • Use Descriptive Titles and Meta Descriptions: Make sure your page titles and meta descriptions accurately reflect the content of your pages. Include relevant keywords.
  • Keyword Research: Identify relevant keywords that users are searching for and incorporate them naturally into your content (headings, product descriptions, etc.).
  • Clean URLs: Use clean and descriptive URLs for your product pages (e.g., `/products/blue-running-shoes` instead of `/products?category=shoes&color=blue`). Next.js provides easy ways to create dynamic routes for this.
  • Image Optimization: Optimize your images for faster loading times by compressing them and using appropriate image formats (e.g., WebP). Use descriptive alt text for your images.
  • Mobile-Friendliness: Ensure your app is responsive and works well on mobile devices.
  • Site Speed: Optimize your app’s loading speed. Fast loading times are crucial for both user experience and SEO.
  • Structured Data: Use structured data markup (schema.org) to provide search engines with more information about your products (e.g., product name, price, availability).

Optional FAQ

Here are some frequently asked questions about product filtering in Next.js:

1. How can I add more filter options?

Simply add more filter options in your `Filter.js` component. You’ll need to update the `handleFilterChange` function to handle the new filter types and update the rendering logic to display the new filter options (e.g., checkboxes, dropdowns, etc.). Remember to add the new filter options to the product data in `data.js` as well.

2. How do I handle multiple filter selections?

In the current implementation, users can only select one option per filter type (e.g., one color). To allow multiple selections, you’ll need to modify the filter component to use checkboxes or multi-select dropdowns instead of radio buttons. You’ll also need to update the `handleFilterChange` function to handle an array of selected values for each filter type. The filtering logic in `handleFilterChange` will also need to be adjusted to check if a product matches *any* of the selected values for a given filter type.

3. How can I implement a price range slider?

You can use a library like `rc-slider` or a custom implementation to create a price range slider. Integrate the slider into your `Filter.js` component and update the `handleFilterChange` function to update the price filter based on the slider’s values. You’ll need to store the minimum and maximum price selected by the user in the filters state.

4. How can I integrate this with an API?

Instead of using static data, you’ll need to fetch your product data from an API. Use `useEffect` in your `pages/index.js` component to fetch the data when the component mounts. Update the `handleFilterChange` function to send the filter criteria to the API and fetch the filtered results.

5. How do I deploy this app?

Next.js apps can be deployed to various platforms, such as Vercel (recommended), Netlify, or AWS. Follow the deployment instructions for your chosen platform. You’ll typically need to build your app (`npm run build`) before deploying.

Building a product filtering app in Next.js is a fantastic project for learning and honing your skills. By following these steps, you’ve created a functional and user-friendly filtering system that can significantly improve the user experience on an e-commerce website. Remember to experiment, customize, and adapt the code to fit your specific needs and design preferences. With a solid understanding of the fundamentals, you can create powerful and engaging web applications that meet the demands of today’s users. The ability to filter products effectively is a core requirement for a successful online store, and the techniques learned in this tutorial are directly transferable to other types of applications that require data filtering and presentation. Continuous learning and experimentation are key to mastering the art of web development, and this project serves as a solid foundation for further exploration.

More posts