Product Listing Overview – Default¶
Atomic Design
Level: Organism · Atomic Design methodology
The default variant displays the Product Listing Overview in a wide horizontal header layout with a persistent sidebar filter menu optimized for desktop and tablet screens (≥ 1024px).
The Default Product Listing Overview variant manages the primary shopping experience for users on desktop viewports. It positions collapsible filter accordions in a left-hand column next to a main grid of products. Active filters are visible as dismissible tags, and sorting options are duplicated at the top and bottom of the grid alongside pagination controls to reduce scrolling fatigue.
For the general composition and the mobile variant, see the Product Listing Overview index.
Composition¶
Used inside: Category Page Template · Search Results Page Template
Composed of: Product Card Molecule · Filter Block (Refinement List · Rangeslider · Size selection · Color selection · Clearrefinement) · Toolbar Block (Sortby · Hits per page · Pagination)
Preview¶

The default desktop layout showing sorting controls, active filters tags, the 3-column product card grid, and the filter sidebar.

Close-up of the filter accordion options, displaying category names, selection checkboxes, and item counts.
Props¶
Top-level props¶
| Prop | Type | Required | Description |
|---|---|---|---|
products |
Product[] |
Yes | Array of product data records matching active filters to populate the grid |
filters |
FilterGroup[] |
Yes | List of available filter groups for rendering the sidebar accordions |
activeFilters |
Record<string, string[]> |
Yes | Key-value mapping of active filter groups and selected options (e.g. {"sekse": ["vrouw"]}) |
sortOptions |
SortOption[] |
Yes | List of available sorting choices |
activeSort |
string |
Yes | Key of the currently active sort option (e.g. "goedkoopste") |
hitsPerPage |
number |
Yes | Number of products displayed per page (e.g. 24) |
hitsPerPageOptions |
number[] |
Yes | List of selectable hits-per-page options (e.g. [24, 48, 96]) |
pagination |
PaginationData |
Yes | Pagination state details (current page, total pages, etc.) |
loading |
boolean |
Yes | Toggles rendering of placeholder card skeletons during API fetch calls |
Nested object props¶
Prop path: filters[]
| Field | Type | Required | Description |
|---|---|---|---|
id |
string |
Yes | Unique identifier slug for the filter group (e.g. "waterdicht") |
name |
string |
Yes | Header display title (e.g. "Waterdicht") |
type |
"list" \| "color" \| "range" \| "size" |
Yes | Determines render template (checkbox list, color grid, price slider, size buttons) |
collapsed |
boolean |
Yes | Controls the open/closed state of the accordion panel |
options |
FilterOption[] |
Yes | List of available filter values |
Prop path: filters[].options[]
| Field | Type | Required | Description |
|---|---|---|---|
value |
string |
Yes | Value sent to search API when checked (e.g. "yes") |
label |
string |
Yes | Label text displayed next to check (e.g. "Waterdicht (35)") |
count |
number |
Yes | Count of matching products (disabled if 0) |
hexCode |
string \| null |
No | Color value hex code (required if parent type is "color") |
Prop path: sortOptions[]
| Field | Type | Required | Description |
|---|---|---|---|
id |
string |
Yes | Sort key identifier (e.g. "goedkoopste") |
label |
string |
Yes | Display label shown in dropdown (e.g. "Prijs laag-hoog") |
Prop path: pagination
| Field | Type | Required | Description |
|---|---|---|---|
currentPage |
number |
Yes | Current active page (1-indexed) |
totalPages |
number |
Yes | Total page count |
totalItems |
number |
Yes | Total count of matched items across all pages |
itemsPerPage |
number |
Yes | Page limit size (e.g. 20) |
Visual States¶
| State | Condition | Visual effect |
|---|---|---|
| Loading Skeletons | loading: true |
Grid fades slightly; cards are replaced by blinking grey skeletons. Accordions and sorting dropdowns are disabled. |
| Sticky Sidebar | Window scroll ≥ header height | The left filter menu sticks to the viewport edge until the grid bottom is reached. |
| Active Filters Tag list | activeFilters is not empty |
A row of pill-shaped tags appears above the grid. Clicking 'x' on a tag clears that filter value. |
| Out-of-Stock Filter | stock.available: false |
Out-of-stock items are visually dimmed or hidden depending on filter selections. |
| Disabled Pagination | pagination.currentPage === 1 |
"Previous Page" arrow triggers are greyed out and pointer events are disabled. |
Behaviour¶
Filter Accordions Animation
Toggling a filter accordion slides the options list down or up using a smooth 200ms ease-in-out transition. Accordion states (expanded/collapsed) are preserved in local storage per user session.
URL State Synchronisation
Any filter checkbox selection or sorting update is instantly serialised into the browser's URL query string (e.g. ?sekse=vrouw&sort=goedkoopste). This enables bookmarks, sharing, and correct browser history "Back" button behavior.
Request Debouncing & Aborting
To prevent network overload when clicking checkboxes quickly, API search requests are debounced by 300ms. If a new filter request is sent before the previous one completes, the active fetch query is aborted.
Example¶
Full example JSON
{
"products": [
{
"id": "scarpa-ribelle-run-gtx",
"name": "Scarpa Ribelle Run GTX",
"category": "Trailrunning schoenen",
"inWishlist": true,
"inCompare": false,
"price": {
"currency": "EUR",
"symbol": "€",
"regular": 249.95,
"sale": 224.95
},
"spec": {
"type": "weight",
"value": 180,
"unit": "gr",
"icon": null
},
"stock": {
"available": true,
"label": "Op voorraad"
},
"variants": [
{
"id": "forest-green",
"color": "#4A7C59",
"label": "Forest Green",
"active": true,
"images": [
{
"src": "/images/scarpa-ribelle-run-gtx-forest-green-1.jpg",
"alt": "Scarpa Ribelle Run GTX – Forest Green"
}
]
}
]
}
],
"filters": [
{
"id": "soorten",
"name": "Soorten",
"type": "list",
"collapsed": false,
"options": [
{ "value": "lage-wandelschoenen", "label": "Lage wandelschoenen", "count": 458 },
{ "value": "trailrunning-schoenen", "label": "Trailrunning schoenen", "count": 58 }
]
},
{
"id": "sekse",
"name": "Sekse",
"type": "list",
"collapsed": false,
"options": [
{ "value": "vrouw", "label": "Vrouw", "count": 312 },
{ "value": "man", "label": "Man", "count": 298 }
]
},
{
"id": "kleur",
"name": "Kleur",
"type": "color",
"collapsed": true,
"options": [
{ "value": "groen", "label": "Groen", "count": 12, "hexCode": "#4A7C59" },
{ "value": "oranje", "label": "Oranje", "count": 5, "hexCode": "#E8673A" }
]
}
],
"activeFilters": {
"soorten": ["trailrunning-schoenen"],
"sekse": ["vrouw"]
},
"sortOptions": [
{ "id": "goedkoopste", "label": "Prijs: laag naar hoog" },
{ "id": "duurste", "label": "Prijs: hoog naar laag" },
{ "id": "nieuwste", "label": "Nieuwste collectie" }
],
"activeSort": "goedkoopste",
"hitsPerPage": 24,
"hitsPerPageOptions": [24, 48, 96],
"pagination": {
"currentPage": 1,
"totalPages": 3,
"totalItems": 58,
"itemsPerPage": 24
},
"loading": false
}