Build a Next.js Interactive Product Catalog with Filtering

Written by

in

In today’s digital landscape, e-commerce is king. Building a functional and user-friendly product catalog is crucial for any online business. But creating one from scratch can seem daunting, especially if you’re new to web development. This article will guide you, step-by-step, through building an interactive product catalog using Next.js, a powerful React framework, enabling you to learn, build, and deploy your own e-commerce-ready application. We will focus on implementing filtering, a critical feature for any product catalog, allowing users to quickly find what they’re looking for.

Why Build a Product Catalog with Next.js?

Next.js offers several advantages for building web applications, especially those focused on content and user experience. Here’s why it’s a great choice for our product catalog:

  • Server-Side Rendering (SSR) and Static Site Generation (SSG): Next.js allows you to render your pages on the server or generate them at build time. This improves SEO (Search Engine Optimization) as search engines can easily crawl and index your content. It also leads to faster initial page loads, enhancing user experience.
  • Optimized Performance: Next.js automatically optimizes images, code splitting, and other performance aspects, resulting in a faster and more efficient website.
  • Easy Routing: Next.js simplifies routing with its file-system-based router. You don’t need to configure complex routing setups; simply create files in the pages directory.
  • Built-in API Routes: Next.js provides a built-in API route feature, making it easy to create and manage your backend API endpoints within the same project.
  • React-Based: If you’re familiar with React, Next.js will feel natural. It uses React components and concepts, making the learning curve smoother.

By using Next.js, we can build a product catalog that is fast, SEO-friendly, and easy to maintain.

Setting Up Your Next.js Project

Let’s get started by setting up a new Next.js project. Open your terminal and run the following command:

npx create-next-app product-catalog

This command will create a new Next.js project named “product-catalog”. Navigate into the project directory:

cd product-catalog

Now, install any necessary dependencies. For this project, we will use a simple dataset in a JSON file to represent our product data, and we’ll use a CSS framework for styling. We’ll use Tailwind CSS for this example, but you can use any framework you prefer (e.g., Bootstrap, Material UI, or even plain CSS).

Install Tailwind CSS and its dependencies:

npm install -D tailwindcss postcss autoprefixer
npx tailwindcss init -p

This will install the necessary packages and create tailwind.config.js and postcss.config.js files.

Next, configure Tailwind CSS by adding the paths to all of your template files in your tailwind.config.js file:

/** @type {import('tailwindcss').Config} */
module.exports = {
  content: [
    './app/**/*.{js,ts,jsx,tsx,mdx}',
    './pages/**/*.{js,ts,jsx,tsx,mdx}',
    './components/**/*.{js,ts,jsx,tsx,mdx}',
  ],
  theme: {
    extend: {
      // Add your custom theme configurations here
    },
  },
  plugins: [],
}

Then, add the Tailwind directives to your global CSS file (usually styles/globals.css):

@tailwind base;
@tailwind components;
@tailwind utilities;

Finally, run the development server:

npm run dev

Your Next.js application is now running on http://localhost:3000. You should see the default Next.js welcome page.

Creating the Product Data

Before we start building the UI, let’s create a simple product data file. Create a new directory called data in the root of your project, and inside it, create a file named products.json. This file will contain an array of product objects. Each product object will have properties like `id`, `name`, `description`, `image`, `price`, and `category`.

[
  {
    "id": 1,
    "name": "Product 1",
    "description": "This is the description for Product 1.",
    "image": "/product1.jpg",
    "price": 25.99,
    "category": "Electronics"
  },
  {
    "id": 2,
    "name": "Product 2",
    "description": "This is the description for Product 2.",
    "image": "/product2.jpg",
    "price": 49.99,
    "category": "Clothing"
  },
  {
    "id": 3,
    "name": "Product 3",
    "description": "This is the description for Product 3.",
    "image": "/product3.jpg",
    "price": 15.50,
    "category": "Electronics"
  },
  {
    "id": 4,
    "name": "Product 4",
    "description": "This is the description for Product 4.",
    "image": "/product4.jpg",
    "price": 75.00,
    "category": "Home Goods"
  },
  {
    "id": 5,
    "name": "Product 5",
    "description": "This is the description for Product 5.",
    "image": "/product5.jpg",
    "price": 30.00,
    "category": "Clothing"
  }
]

Make sure to replace the image paths (e.g., “/product1.jpg”) with actual image paths. You can either put images in your public directory or use URLs for images hosted elsewhere.

Building the Product Listing Component

Now, let’s create a component to display the products. Create a new directory called components in your project’s root. Inside the components directory, create a file named ProductCard.js. This component will be responsible for displaying a single product.

import Image from 'next/image';

function ProductCard({ product }) {
  return (
    <div>
      
      <h3>{product.name}</h3>
      <p>{product.description}</p>
      <p>${product.price.toFixed(2)}</p>
    </div>
  );
}

export default ProductCard;

This component takes a `product` object as a prop and renders its details. We use the Next.js `Image` component for optimized image loading. Next, create another component called `ProductList.js` in the `components` directory. This component will fetch the product data and render the `ProductCard` components.

import ProductCard from './ProductCard';
import { useState, useEffect } from 'react';

function ProductList() {
  const [products, setProducts] = useState([]);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState(null);

  useEffect(() => {
    async function fetchProducts() {
      try {
        const response = await fetch('/api/products'); // Assuming you create an API route
        if (!response.ok) {
          throw new Error(`HTTP error! status: ${response.status}`);
        }
        const data = await response.json();
        setProducts(data);
      } catch (err) {
        setError(err);
      } finally {
        setLoading(false);
      }
    }

    fetchProducts();
  }, []);

  if (loading) return <p>Loading products...</p>;
  if (error) return <p>Error loading products: {error.message}</p>;

  return (
    <div>
      {products.map((product) => (
        
      ))}
    </div>
  );
}

export default ProductList;

This component fetches the product data from our API route (we’ll create this soon) and renders a `ProductCard` for each product. It also handles loading and error states.

Creating the API Route for Product Data

Next.js makes it easy to create API routes. Create a directory named pages/api in your project. Inside this directory, create a file named products.js. This file will handle requests to fetch the product data.

import productsData from '../../data/products.json';

export default function handler(req, res) {
  res.status(200).json(productsData);
}

This simple API route imports our products.json data and returns it as a JSON response. Now, the `ProductList` component will fetch the data from /api/products.

Integrating the Components into the Page

Now, let’s integrate our components into the main page. Open pages/index.js and modify it as follows:

import ProductList from '../components/ProductList';

export default function Home() {
  return (
    <div>
      <h1>Product Catalog</h1>
      
    </div>
  );
}

This imports the `ProductList` component and renders it on the home page. The `container mx-auto p-4` and other Tailwind classes provide basic styling and layout. Run your development server (npm run dev) and navigate to http://localhost:3000. You should now see your product catalog, displaying the product cards.

Implementing Product Filtering

The core of this article is implementing product filtering. We’ll add a filter component that allows users to filter products by category. Create a new component file called Filter.js in the components directory.

import { useState, useEffect } from 'react';

function Filter({ products, onFilter }) {
  const [categories, setCategories] = useState([]);
  const [selectedCategory, setSelectedCategory] = useState('All');

  useEffect(() => {
    const uniqueCategories = ['All', ...new Set(products.map(product => product.category))];
    setCategories(uniqueCategories);
  }, [products]);

  const handleCategoryChange = (event) => {
    const category = event.target.value;
    setSelectedCategory(category);
    onFilter(category);
  };

  return (
    <div>
      <label>Filter by Category:</label>
      
        {categories.map(category => (
          {category}
        ))}
      
    </div>
  );
}

export default Filter;

This component takes `products` and `onFilter` as props. It extracts unique categories from the products data, renders a dropdown with these categories, and calls the `onFilter` function when the user selects a category. Now, modify the `ProductList` component to use the `Filter` component and filter the products.

import ProductCard from './ProductCard';
import Filter from './Filter';
import { useState, useEffect } from 'react';

function ProductList() {
  const [products, setProducts] = useState([]);
  const [filteredProducts, setFilteredProducts] = useState([]);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState(null);

  useEffect(() => {
    async function fetchProducts() {
      try {
        const response = await fetch('/api/products');
        if (!response.ok) {
          throw new Error(`HTTP error! status: ${response.status}`);
        }
        const data = await response.json();
        setProducts(data);
        setFilteredProducts(data); // Initially, show all products
      } catch (err) {
        setError(err);
      } finally {
        setLoading(false);
      }
    }

    fetchProducts();
  }, []);

  const handleFilter = (category) => {
    if (category === 'All') {
      setFilteredProducts(products);
    } else {
      const filtered = products.filter(product => product.category === category);
      setFilteredProducts(filtered);
    }
  };

  if (loading) return <p>Loading products...</p>;
  if (error) return <p>Error loading products: {error.message}</p>;

  return (
    <div>
      
      <div>
        {filteredProducts.map((product) => (
          
        ))}
      </div>
    </div>
  );
}

export default ProductList;

We’ve added a `filteredProducts` state variable to hold the filtered product list. The `handleFilter` function updates this state based on the selected category in the `Filter` component. We now pass the original `products` data to the `Filter` component. Finally, update pages/index.js to import and use the `ProductList` component.

With these changes, your product catalog now includes a functional category filter! You can select different categories from the dropdown, and the product list will update accordingly. This provides a much better user experience than a static list of products.

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 data. Typos can easily break your application.
  • CSS Issues: Ensure your CSS framework (e.g., Tailwind CSS) is correctly configured and that you’ve included the necessary directives in your global CSS file. If styles aren’t applying, verify the CSS class names and that the CSS file is being imported correctly.
  • API Route Errors: If your API route isn’t working, check the console for any errors. Ensure the file path for your API route is correct (e.g., pages/api/products.js) and that the data is being returned correctly. Use console.log() statements within your API route to debug and see what data is being returned.
  • Data Fetching Problems: If your data isn’t loading, check the browser’s developer console for any network errors. Verify the URL of your API route and that it’s returning the correct data format (JSON). Also, make sure the `useEffect` hook has the correct dependencies.
  • Filtering Logic Errors: If the filtering isn’t working as expected, carefully review the `handleFilter` function and the filtering logic within the `ProductList` component. Use `console.log()` to check the values being compared.

SEO Best Practices

To ensure your product catalog ranks well in search results, consider these SEO best practices:

  • Descriptive Titles and Meta Descriptions: Use clear, concise, and keyword-rich titles and meta descriptions for your pages. The title should be within 60 characters and the meta description within 160 characters.
  • Keyword Research: Identify relevant keywords that your target audience is using to search for products. Incorporate these keywords naturally into your content, including product descriptions, titles, and headings.
  • Optimized Image Alt Text: Add descriptive alt text to your images, including relevant keywords. This helps search engines understand the content of your images and improves accessibility.
  • Fast Loading Speed: Next.js helps with performance, but ensure your images are optimized, and your code is efficient. Use tools like Lighthouse to analyze your website’s performance.
  • Mobile-Friendly Design: Ensure your product catalog is responsive and works well on all devices, especially mobile phones.
  • Structured Data Markup: Use structured data (schema.org) to provide search engines with more information about your products, such as product names, prices, and availability.
  • Internal Linking: Link to other relevant pages within your product catalog to improve navigation and distribute link juice.

Key Takeaways

You’ve successfully built a Next.js product catalog with filtering! This project demonstrates how to use Next.js’s features, including server-side rendering, API routes, and component-based architecture. You’ve learned how to:

  • Set up a Next.js project.
  • Create and structure product data.
  • Build reusable components for displaying products.
  • Implement API routes to fetch data.
  • Add a functional category filter.

By following these steps, you’ve gained valuable skills in front-end development, e-commerce principles, and the power of Next.js. You now have a solid foundation for building more complex and feature-rich e-commerce applications.

This project is a starting point. You can extend it by adding features like product details pages, shopping carts, user authentication, and more advanced filtering options. Consider adding sorting functionality, pagination for large product sets, and a search bar to enhance the user experience further. Remember to continuously test and refine your application to ensure it meets your users’ needs and performs optimally. Happy coding!