Popular Searches
Popular Course Categories
Popular Courses

Product Listing Application

Product Listing Application

React JS Practical Projects

Product Listing Application in React JS

A Product Listing Application is a React JS project that displays multiple products in an organized layout. Each product can include an image, name, price, category, rating, and an action button. This project helps developers understand React components, props, state management, array methods, search, filtering, sorting, and responsive UI design.

React JS Course: JustAcademy React JS Training Course

Course Demo: Register for React JS Course Demo


1. Learning Objectives

  • Understand the structure of a product listing application.
  • Create reusable product cards using React components.
  • Display product data from an array of objects.
  • Use map() to render multiple products.
  • Use filter() to search and filter products.
  • Implement sorting by price and product name.
  • Manage user interactions with useState.
  • Use props to pass product information between components.
  • Create a responsive product grid using CSS.
  • Implement an Add to Cart interaction.

2. What Is a Product Listing Application?

A Product Listing Application displays a collection of products so users can browse, search, filter, compare, and select items. It is commonly used in e-commerce websites, online shopping platforms, electronics stores, fashion stores, and grocery applications.

Key Features

  • Product image and name.
  • Product price and rating.
  • Category-based filtering.
  • Search by product name.
  • Sorting by price or name.
  • Add to Cart button.
  • Product count and empty-result message.
  • Responsive layout for mobile, tablet, and desktop.

3. Technologies Used

Technology Purpose
React JS Builds the product listing interface.
JavaScript Handles product data, search, filtering, and sorting.
JSX Describes the UI using HTML-like syntax.
CSS Styles product cards and layouts.
useState Stores search terms, filters, sorting, and cart state.
Props Passes product data to reusable components.
map() Creates product cards from an array.
filter() Selects products matching search and category conditions.
sort() Arranges products in a selected order.

4. Project Structure

Create a React application using Vite:

npm create vite@latest product-listing -- --template react
cd product-listing
npm install
npm run dev

Recommended folder structure:

product-listing/
├── src/
│   ├── components/
│   │   └── ProductCard.jsx
│   ├── App.jsx
│   ├── App.css
│   ├── index.css
│   └── main.jsx
├── package.json
└── index.html

5. Create Product Data

Each product is represented by an object containing a unique ID, name, price, category, rating, and image URL. For this beginner project, the data can be stored in an array.

const products = [
  {
    id: 1,
    name: "Wireless Headphones",
    price: 2499,
    category: "Electronics",
    rating: 4.5,
    image: "https://placehold.co/400x300?text=Headphones"
  },
  {
    id: 2,
    name: "Running Shoes",
    price: 1999,
    category: "Fashion",
    rating: 4.2,
    image: "https://placehold.co/400x300?text=Shoes"
  },
  {
    id: 3,
    name: "Smart Watch",
    price: 3499,
    category: "Electronics",
    rating: 4.6,
    image: "https://placehold.co/400x300?text=Smart+Watch"
  },
  {
    id: 4,
    name: "Travel Backpack",
    price: 1299,
    category: "Accessories",
    rating: 4.1,
    image: "https://placehold.co/400x300?text=Backpack"
  },
  {
    id: 5,
    name: "Cotton T-Shirt",
    price: 599,
    category: "Fashion",
    rating: 4.0,
    image: "https://placehold.co/400x300?text=T-Shirt"
  },
  {
    id: 6,
    name: "Bluetooth Speaker",
    price: 1599,
    category: "Electronics",
    rating: 4.3,
    image: "https://placehold.co/400x300?text=Speaker"
  }
];

Explanation

  • id uniquely identifies each product.
  • name stores the product title.
  • price stores the product price as a number.
  • category identifies the product type.
  • rating stores the product rating.
  • image stores the image URL.

The sample image URLs are placeholders. Replace them with appropriate product images in a real project.


6. Create the ProductCard Component

A reusable ProductCard component displays the details of one product. React props allow the parent component to pass product information and button actions to each card.

Create src/components/ProductCard.jsx:

function ProductCard({ product, onAddToCart }) {
  return (
    <article className="product-card">
      <img
        src={product.image}
        alt={product.name}
        className="product-image"
        loading="lazy"
      />
      <div className="product-info">
        <p className="product-category">
          {product.category}
        </p>
        <h3>{product.name}</h3>
        <p className="product-rating">
          Rating: {product.rating} / 5
        </p>
        <p className="product-price">
          ₹{product.price.toLocaleString("en-IN")}
        </p>
        <button
          type="button"
          onClick={() => onAddToCart(product)}
        >
          Add to Cart
        </button>
      </div>
    </article>
  );
}
export default ProductCard;

Why Use a Reusable Component?

  • Reduces repeated code.
  • Makes product cards easier to maintain.
  • Allows the same component to display different products.
  • Keeps the main application component organized.

7. Build the Main Application in App.jsx

The main component manages the product data, search input, category selection, sorting option, and cart. The displayed product list is calculated from the original data whenever the search, category, or sort selection changes.

import { useState } from "react";
import ProductCard from "./components/ProductCard";
import "./App.css";

const products = [
  {
    id: 1,
    name: "Wireless Headphones",
    price: 2499,
    category: "Electronics",
    rating: 4.5,
    image: "https://placehold.co/400x300?text=Headphones"
  },
  {
    id: 2,
    name: "Running Shoes",
    price: 1999,
    category: "Fashion",
    rating: 4.2,
    image: "https://placehold.co/400x300?text=Shoes"
  },
  {
    id: 3,
    name: "Smart Watch",
    price: 3499,
    category: "Electronics",
    rating: 4.6,
    image: "https://placehold.co/400x300?text=Smart+Watch"
  },
  {
    id: 4,
    name: "Travel Backpack",
    price: 1299,
    category: "Accessories",
    rating: 4.1,
    image: "https://placehold.co/400x300?text=Backpack"
  },
  {
    id: 5,
    name: "Cotton T-Shirt",
    price: 599,
    category: "Fashion",
    rating: 4.0,
    image: "https://placehold.co/400x300?text=T-Shirt"
  },
  {
    id: 6,
    name: "Bluetooth Speaker",
    price: 1599,
    category: "Electronics",
    rating: 4.3,
    image: "https://placehold.co/400x300?text=Speaker"
  }
];

function App() {
  const [search, setSearch] = useState("");
  const [category, setCategory] = useState("All");
  const [sortBy, setSortBy] = useState("default");
  const [cart, setCart] = useState([]);

  function addToCart(product) {
    setCart((currentCart) => {
      const existing = currentCart.find(
        (item) => item.id === product.id
      );

      if (existing) {
        return currentCart.map((item) =>
          item.id === product.id
            ? { ...item, quantity: item.quantity + 1 }
            : item
        );
      }

      return [...currentCart, { ...product, quantity: 1 }];
    });
  }

  const filteredProducts = products
    .filter((product) =>
      product.name.toLowerCase().includes(search.trim().toLowerCase())
    )
    .filter((product) =>
      category === "All" || product.category === category
    );

  const sortedProducts = [...filteredProducts].sort((a, b) => {
    if (sortBy === "price-low") return a.price - b.price;
    if (sortBy === "price-high") return b.price - a.price;
    if (sortBy === "name") return a.name.localeCompare(b.name);
    return 0;
  });

  const cartCount = cart.reduce(
    (total, item) => total + item.quantity,
    0
  );

  return (
    <main className="shop-page">
      <header className="shop-header">
        <div>
          <h1>Shop Products</h1>
          <p>Find products for your everyday needs.</p>
        </div>
        <div className="cart-count" aria-live="polite">
          Cart: {cartCount}
        </div>
      </header>

      <section className="filters" aria-label="Product filters">
        <input
          type="search"
          value={search}
          onChange={(event) => setSearch(event.target.value)}
          placeholder="Search products..."
          aria-label="Search products"
        />

        <select
          value={category}
          onChange={(event) => setCategory(event.target.value)}
          aria-label="Filter by category"
        >
          <option value="All">All Categories</option>
          <option value="Electronics">Electronics</option>
          <option value="Fashion">Fashion</option>
          <option value="Accessories">Accessories</option>
        </select>

        <select
          value={sortBy}
          onChange={(event) => setSortBy(event.target.value)}
          aria-label="Sort products"
        >
          <option value="default">Default Order</option>
          <option value="price-low">Price: Low to High</option>
          <option value="price-high">Price: High to Low</option>
          <option value="name">Name: A to Z</option>
        </select>
      </section>

      <p className="result-count" aria-live="polite">
        Showing {sortedProducts.length} products
      </p>

      {sortedProducts.length > 0 ? (
        <section className="product-grid" aria-label="Products">
          {sortedProducts.map((product) => (
            <ProductCard
              key={product.id}
              product={product}
              onAddToCart={addToCart}
            />
          ))}
        </section>
      ) : (
        <p className="empty-state">
          No products found. Try another search or category.
        </p>
      )}
    </main>
  );
}

export default App;

Explanation of App.jsx

  • useState stores the search term, selected category, sorting option, and cart items.
  • filter() selects products matching the search term and selected category.
  • sort() arranges products by price or name.
  • map() renders a ProductCard for each product.
  • key={product.id} provides a stable identifier for each rendered card.
  • onAddToCart passes the add-to-cart function to each card.
  • reduce() calculates the total number of items in the cart.
  • Conditional rendering displays either the product grid or an empty-state message.

8. Style the Application with App.css

.shop-page {
  max-width: 1200px;
  margin: 0 auto;
  padding: 28px 20px;
  font-family: Arial, sans-serif;
  color: #222;
}

.shop-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 16px;
  margin-bottom: 24px;
}

.shop-header h1 {
  margin: 0 0 8px;
}

.shop-header p {
  color: #666;
  margin: 0;
}

.cart-count {
  padding: 12px 16px;
  border-radius: 8px;
  background: #e8f2ff;
  color: #0759a5;
  font-weight: bold;
  white-space: nowrap;
}

.filters {
  display: grid;
  grid-template-columns: 2fr 1fr 1fr;
  gap: 12px;
  margin-bottom: 18px;
}

.filters input,
.filters select {
  width: 100%;
  min-width: 0;
  padding: 12px;
  border: 1px solid #d5d9df;
  border-radius: 8px;
  background: white;
  font-size: 15px;
}

.result-count {
  color: #555;
  margin: 18px 0;
}

.product-grid {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 22px;
}

.product-card {
  overflow: hidden;
  border: 1px solid #e2e5e9;
  border-radius: 14px;
  background: white;
  transition: transform 0.2s ease, box-shadow 0.2s ease;
}

.product-card:hover {
  transform: translateY(-4px);
  box-shadow: 0 8px 24px rgba(0, 0, 0, 0.1);
}

.product-image {
  display: block;
  width: 100%;
  height: 210px;
  object-fit: cover;
  background: #f4f6f8;
}

.product-info {
  padding: 16px;
}

.product-category {
  color: #777;
  font-size: 13px;
  text-transform: uppercase;
  letter-spacing: 0.5px;
}

.product-info h3 {
  margin: 8px 0;
}

.product-rating {
  color: #9a6900;
}

.product-price {
  font-size: 21px;
  font-weight: bold;
  margin: 12px 0;
}

.product-info button {
  width: 100%;
  padding: 12px;
  border: none;
  border-radius: 8px;
  background: #0969da;
  color: white;
  cursor: pointer;
  font-weight: bold;
}

.product-info button:hover {
  background: #0759b8;
}

.product-info button:focus-visible,
.filters input:focus-visible,
.filters select:focus-visible {
  outline: 3px solid #80b8ff;
  outline-offset: 2px;
}

.empty-state {
  padding: 36px 16px;
  text-align: center;
  border-radius: 12px;
  background: #f5f7fa;
  color: #555;
}

@media (max-width: 900px) {
  .product-grid {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }

  .filters {
    grid-template-columns: 1fr 1fr;
  }

  .filters input {
    grid-column: 1 / -1;
  }
}

@media (max-width: 560px) {
  .shop-page {
    padding: 18px 12px;
  }

  .shop-header {
    align-items: flex-start;
    flex-direction: column;
  }

  .filters {
    grid-template-columns: 1fr;
  }

  .filters input {
    grid-column: auto;
  }

  .product-grid {
    grid-template-columns: 1fr;
  }

  .product-image {
    height: 240px;
  }
}

9. Add Global CSS in index.css

* {
  box-sizing: border-box;
}

body {
  margin: 0;
  background: #f8fafc;
}

button,
input,
select {
  font: inherit;
}

button:disabled {
  cursor: not-allowed;
  opacity: 0.6;
}

10. Important React Concepts Used

10.1 Components

Components are reusable UI building blocks. In this project, App manages the overall page and ProductCard displays an individual product.

10.2 Props

Props pass data and callback functions from a parent component to a child component.

<ProductCard
  product={product}
  onAddToCart={addToCart}
/>

10.3 useState

useState stores information that changes as users interact with the application.

const [search, setSearch] = useState("");
const [category, setCategory] = useState("All");

10.4 map()

The map() method transforms each product object into a React component.

{products.map((product) => (
  <ProductCard key={product.id} product={product} />
))}

10.5 filter()

The filter() method returns a new array containing only products that satisfy a condition.

const affordableProducts = products.filter(
  (product) => product.price < 2000
);

10.6 sort()

The sort() method orders products. Because sort modifies the array on which it runs, create a copy before sorting state-derived or shared data.

const lowToHigh = [...products].sort(
  (a, b) => a.price - b.price
);

11. How Search Works

The search field updates the search state whenever the user types. The application compares the entered text with each product name without considering uppercase or lowercase letters.

const matchingProducts = products.filter((product) =>
  product.name.toLowerCase().includes(search.trim().toLowerCase())
);

For example, searching for head matches the product named Wireless Headphones.


12. How Category Filtering Works

Category filtering displays only products that belong to the selected category.

const filteredProducts = products.filter((product) =>
  category === "All" || product.category === category
);

If the user selects Electronics, only products in the Electronics category are displayed.


13. How Sorting Works

Option Logic Result
Price: Low to High a.price - b.price Cheapest products appear first.
Price: High to Low b.price - a.price Most expensive products appear first.
Name: A to Z a.name.localeCompare(b.name) Products appear alphabetically.
Default Order Returns comparison value 0 Preserves the original relative order in this example.

14. How Add to Cart Works

When the user clicks Add to Cart, the selected product is added to cart state. If that product already exists in the cart, its quantity increases by one.

function addToCart(product) {
  setCart((currentCart) => {
    const existing = currentCart.find(
      (item) => item.id === product.id
    );

    if (existing) {
      return currentCart.map((item) =>
        item.id === product.id
          ? { ...item, quantity: item.quantity + 1 }
          : item
      );
    }

    return [...currentCart, { ...product, quantity: 1 }];
  });
}

This example updates the cart count but does not yet include a cart page, checkout, payment, or persistent storage. Those can be added as separate features.


15. Product Listing Application Workflow

Start Application
       |
       v
Load Product Data
       |
       v
Display Product Cards
       |
       v
User Searches or Selects Category
       |
       v
Filter Matching Products
       |
       v
Apply Sorting Option
       |
       v
Render Updated Product Grid
       |
       v
User Clicks Add to Cart
       |
       v
Update Cart State and Count

16. Common Errors and Solutions

Error Solution
Products do not display Check the product array and the map() expression.
Missing key warning Provide a stable unique key such as product.id.
Search is case-sensitive Convert both strings using toLowerCase().
Sorting changes original data Sort a copy using [...array].sort().
Filter does not update Use controlled inputs and update state in onChange.
Images do not load Verify the URL and provide appropriate fallback handling.
Cart quantity is incorrect Check product IDs and use a functional state update.
Mobile layout looks crowded Use CSS Grid and responsive media queries.

17. Best Practices

  • Use reusable components for product cards.
  • Use stable, unique product IDs as React keys.
  • Keep product data separate from presentation when appropriate.
  • Use controlled inputs for search and filters.
  • Avoid mutating arrays that are held in state or shared between components.
  • Provide meaningful image alt text.
  • Display a clear empty state when no products match.
  • Format prices consistently using the appropriate currency.
  • Make buttons and form controls accessible by keyboard.
  • Use real product data from an API or backend for production applications.
  • Keep cart logic separate from UI code as the application grows.

18. Testing Checklist

  • Verify that all sample products display correctly.
  • Search using a complete product name.
  • Search using only part of a product name.
  • Test category filtering.
  • Test each sorting option.
  • Search for a product that does not exist.
  • Add the same product to the cart multiple times.
  • Verify the cart count updates correctly.
  • Test the layout on desktop, tablet, and mobile.
  • Check keyboard accessibility and image loading.

19. Interview Questions and Answers

Q1. What is a Product Listing Application?

It is an application that displays products with details such as images, names, prices, categories, and ratings.

Q2. Why do we use map() in React?

We use map() to transform an array of data into a collection of React elements.

Q3. What is the purpose of a key prop?

A key helps React identify items in a rendered list when items are added, removed, or reordered.

Q4. What is the difference between map() and filter()?

map() transforms every array element, while filter() returns only elements that satisfy a condition.

Q5. Why do we use useState?

useState stores changing values such as search terms, selected categories, and cart contents.

Q6. What are props in React?

Props are inputs passed from a parent component to a child component.

Q7. How does product search work?

The app compares the search term with product names and displays matching products using filter().

Q8. How can products be sorted by price?

Use sort() with a numeric comparison such as (a, b) => a.price - b.price for ascending order.

Q9. Why should we copy an array before sorting?

sort() modifies its array. Copying first prevents accidental mutation of the original product data.

Q10. How can the product list be made responsive?

Use CSS Grid or Flexbox along with media queries to adapt the number of columns to the screen width.

Q11. How can product data come from an API?

Fetch the data from a backend or product API, manage loading and error states, and render the returned array.

Q12. How does Add to Cart work?

A click handler updates cart state by adding a product or increasing its existing quantity.


20. Practical Exercises

  1. Add a brand filter to the product listing.
  2. Add a minimum and maximum price filter.
  3. Display a discount percentage for selected products.
  4. Add a product details page.
  5. Create a separate shopping cart page.
  6. Add quantity increase and decrease controls.
  7. Fetch products from a backend API.
  8. Add pagination or a Load More button.
  9. Save the cart to localStorage.
  10. Write tests for searching, filtering, sorting, and cart updates.

21. Quick Revision

  • React components: Build reusable UI sections.
  • Props: Pass product information and callbacks.
  • useState: Manages interactive values.
  • map(): Renders product cards.
  • filter(): Searches and filters products.
  • sort(): Organizes products by price or name.
  • key: Identifies each product in a rendered list.
  • CSS Grid: Creates a responsive product layout.
  • Cart state: Tracks selected products and quantities.

22. Official Documentation and Learning Resources


23. React JS Course and Demo Links

Learn more about React components, state management, props, and practical application development using these course resources:

Conclusion

The Product Listing Application is a practical React JS project that demonstrates reusable components, props, state management, array methods, search, filtering, sorting, and cart interactions. Completing this project provides a foundation for developing more advanced e-commerce applications with APIs, product details, shopping carts, and checkout features.

whatsapp