---
title: "Building a product listing page with Search.js"
description: "Use the Search.js library to rapidly build a feature-rich, customizable product listing experience with minimal code."
slug: quickstart/product-listing/with-searchjs
docKind: guide
hub: quickstart
---

## Introduction

While the [Luigi's Box API](/product-listing/api/) provides maximum control, the [Search.js](/search/search-js/) library offers the fastest path to a feature-rich and fully interactive product listing page (PLP). By using Search.js for listings, you can render a complete product listing page, including dynamic filters, sorting, and pagination, with just a few lines of JavaScript.
This guide will show you how to correctly initialize the [Search.js](/search/search-js/) widget and then execute a listing for a specific category, effectively turning the search UI into a powerful category page.


### What you'll learn

- How to include and initialize the [Search.js](/search/search-js/) library with essential configurations.
- How to execute a product listing for a specific category using [Search.js](/search/search-js/).

### Who is this guide for

- Developers looking for a fast, out-of-the-box solution for a product listing page.

### Prerequisites

- Basic knowledge of HTML and JavaScript.
- Your Luigi's Box `TrackerId`.

:::note[Runnable example HTML]
Open or download the full standalone sample: [searchjs.html](/examples/product-listing/searchjs.html)
:::

## Step-by-step: building the product listing page


### Step 1: Create a new product listing page

First, you need to add two key elements to your HTML page: an `<input>` for the search bar and a `<div>` that will act as a container for the entire product listing user interface.

[Search.js](/search/search-js/) requires a search input for initialization, even if the primary purpose of the page is to display a category listing.

#### Example: `plp.html` body

```html
<body>

    <h1>Product Listing</h1>

    <!-- A search input is required for Search.js to initialize -->
    <input type="search" id="search-input" placeholder="Search within our products...">

    <!-- The container where the Product Listing UI will be rendered -->
    <div id="plp-ui-container">
        <p>Loading products...</p>
    </div>

    <!-- The Search.js library will be added in the next step -->
    <script src="https://cdn.luigisbox.com/search.js"></script>
    <script>
        // The initialization script will go here
    </script>

</body>
</html>
```

### Step 2: Initialize and execute the listing

A key aspect of using [Search.js](/search/search-js/) for product listings is understanding its two-step process:

1. **Initialization (`Luigis.Search({...})`)**: The first call you make is to `Luigis.Search()`. Its job is to initialize the entire search interface. It sets up all the components like facets and sorting and links the library to your HTML placeholders. At this stage, the UI is ready, but no search has been performed.
2. **Execution  (`Luigis.Search.Search(...)`)**: The second call you make is to the `Search()` method on the already-initialized `Luigis.Search` object. Its job is to execute the initial search. For a listing page, you'll execute this with a `null` query but with a special `IntentFilters` object that tells the system what category to display.

Add the following script to your HTML file, just before the closing `</body>` tag.

#### Example: initialization and execution script

```html
<script>
  document.addEventListener('DOMContentLoaded', function() {
    // 1. First, INITIALIZE the search UI.
    Luigis.Search({
      TrackerId: '111111-111111', // Replace with your Tracker ID
      Theme: 'boo',
      Facets: ['brand', 'price_amount'],
      DefaultFilters: {
          type: 'product'
      }
    },
    '#search-input',
    '#plp-ui-container'
    );

    // 2. Immediately after, EXECUTE the search for the category.
    Luigis.Search.Search(
      null, // Query is null for a listing
      {
        ProductListingFilter: "category",
        IntentFilters: {
          category: "Guitars"
        }
      }
    );
  });
</script>
```
This script does the following:

- Initializes the Search.js library with your `Tracker ID` and sets the theme to `'boo'`.
- Sets the facets to be displayed (e.g., brand and price).
- Defines the default filter to only show products.
- Executes the search with a `null` query, specifying that you want to filter by the "Guitars" category.


### Step 3: Handling multiple product listing pages

The example above is perfect for a single page. But what if your website has many standalone HTML files for different brands and categories (e.g., `yamaha.html`, `guitars.html`)? You need a scalable approach.

The best practice is to create a single, reusable JavaScript file that can intelligently configure itself based on which page it's on, using HTML `data-` attributes.

#### Example: brand product listing page (`yamaha.html`) HTML setup

```html
<!DOCTYPE html>
<html lang="en">
<body data-plp-type="brand" data-plp-value="Yamaha">

    <h1>Yamaha Products</h1>
    <input type="search" id="search-input" style="display: none;" />
    <div id="plp-ui-container">Loading...</div>

    <!-- Load the libraries -->
    <script src="https://cdn.luigisbox.com/search.js"></script>
    <!-- Load YOUR reusable script -->
    <script src="/scripts/plp-loader.js"></script>

</body>
</html>
```

#### Example: category product listing page (`guitars.html`) HTML setup

```html
<!DOCTYPE html>
<html lang="en">
<body data-plp-type="category" data-plp-value="Guitars">

    <h1>Guitar Products</h1>
    <input type="search" id="search-input" style="display: none;" />
    <div id="plp-ui-container">Loading...</div>

    <!-- Load the libraries -->
    <script src="https://cdn.luigisbox.com/search.js"></script>
    <!-- Load YOUR reusable script -->
    <script src="/scripts/plp-loader.js"></script>

</body>
</html>
```

#### Example: reusable product listing loader script (`plp-loader.js`)

Now, create one JavaScript file that will be included on every product listing page. This script reads the `data-` attributes from the body and configures [Search.js](/search/search-js/) dynamically.

```javascript
// File: /scripts/plp-loader.js

document.addEventListener('DOMContentLoaded', function() {
    const body = document.body;
    const filterType = body.dataset.plpType;   // Reads "data-plp-type"
    const filterValue = body.dataset.plpValue; // Reads "data-plp-value"

    // If the required data attributes aren't on this page, do nothing.
    if (!filterType || !filterValue) {
        return;
    }

    // 1. Initialize the UI (same configuration for all pages)
    Luigis.Search({
        TrackerId: '111111-111111', // Your Tracker ID
        Theme: 'boo',
        Facets: ['brand', 'price_amount'],
        DefaultFilters: {
            type: 'product'
        }
    },
    '#search-input',
    '#plp-ui-container'
    );

    // 2. Execute the listing using the dynamic values from the HTML
    Luigis.Search.Search(
        null,
        {
            ProductListingFilter: filterType,
            // Use the dynamic values to build the IntentFilters object
            IntentFilters: {
                [filterType]: filterValue
            }
        }
    );
});
```

## Best practices

- **Match the `ProductListingFilter`:** Ensure the value of `ProductListingFilter` exactly matches the key you are using in `IntentFilters`. This is how Luigi's Box applies your merchandising rules, and a mismatch is a common source of errors.
- **Use a theme:** The `Theme: 'boo'` setting is the recommended way to apply modern, responsive styling to the widget. It provides a solid foundation that you can then customize with your own CSS or by overriding the default templates.
- **Plan for a universal search bar:** While the multi-page example requires an `<input>` on each page, the ideal architecture for a real website is a shared layout or header that contains a single, universal search bar. This provides a more consistent user experience and simplifies your code.
- **Customize with templates:** For full control over the look and feel, Search.js offers a powerful templating system. You can override the default HTML of any component, from the product tiles to the facet filters.

## Next steps

Now that you have a working and scalable product listing page, you can customize its appearance and explore more advanced features.

- **Customize the UI:** The Search.js library provides a powerful templating system that gives you full control over the HTML of every component. Learn how in our [Customizing the Search.js UI](/quickstart/search/customizing-searchjs/) guide.
