Product Listing Page (PLP)

Collection and search results pages with filtering, sorting, and pagination.

vercel.shop/collections/all
S
DCart
DResults

The PLP covers two routes - /collections/[handle] for browsing a collection and /search for query-based product search. Both share the same grid, filtering, sorting, and pagination components.

Product grid

Products display in a responsive grid: 2 columns on mobile, 3 on tablet, 4 on desktop. Each page shows up to 50 products. During filter or sort transitions, a ProductGridPendingOverlay dims the grid to signal loading.

Each product renders as a card with the featured image, title, price, compare-at price, availability status, and a quick-add button for in-stock items.

Filtering

A sidebar provides multi-select faceted filters for:

  • Size, Color, Vendor, Product Type, Tags - checkbox-based multi-select
  • Price range - slider with 4 presets (Under $50, $50–$100, $100–$200, Over $200)

Active filters appear as badges with remove buttons. Filter state is encoded in URL search params using Shopify's standard format (?filter.v.option.color=red&filter.v.price.gte=10&filter.v.price.lte=100) so filtered views are shareable and compatible with Shopify themes.

Filters use useOptimistic for instant UI feedback while the server request is in flight, coordinated through a FilterTransitionProvider context.

On mobile, filters are accessible through a sheet overlay rather than a persistent sidebar.

Sorting

A select dropdown offers five sort options:

  • Best Matches (default)
  • Price: Low to High
  • Price: High to Low
  • Name: A to Z
  • Name: Z to A

Sort changes use useTransition for non-blocking navigation. On mobile, the sort control renders as a bottom sheet.

Pagination

Pagination is cursor-based using Shopify's endCursor rather than page numbers. A First/Next button pair navigates between pages, with loading indicators and disabled states at boundaries. The cursor resets automatically when filters or sort change.

Collection pages

The /collections/[handle] route fetches collection metadata (title, description, image, SEO) and its products in parallel. Collection data is cached with cacheLife("max") and tagged for granular revalidation (collection-{handle}).

Products within a collection are fetched via getCollectionProducts(), which accepts Shopify ProductFilter[] for server-side filtering by variant options, price, vendor, type, tags, and metafields.

Note: Filters are driven entirely by Shopify's ProductFilter type. Adding custom filter logic (e.g., filtering by a metafield) requires modifying the GraphQL query in lib/shopify/operations/products.ts.

Search page

The /search route accepts a q query parameter and combines it with the same filter and sort controls. getProducts() builds a Shopify search query from the text input and active filters, mapping UI sort keys to Shopify's RELEVANCE and PRICE sort keys.

The search page displays a custom header with the query and result count, and shows a "no results" message with i18n support when the query returns empty.

Key files

FilePurpose
app/collections/[handle]/page.tsxCollection route with parallel data fetching
app/search/page.tsxSearch route with query param handling
components/collections/results-grid.tsxResponsive product grid with loading overlay
components/filters/collection-filter-sidebar.tsxFaceted filter sidebar with optimistic UI
components/collections/sort-select.tsxSort dropdown (desktop) / bottom sheet (mobile)
components/collections/pagination.tsxCursor-based pagination controls
components/product-card.tsxProduct card with image, hover slideshow, price, quick-add
components/search/results.tsxSearch results layout with no-results state
lib/shopify/operations/products.tsProduct search with caching and filter mapping
lib/shopify/operations/collections.tsCollection metadata fetching

Chat

Tip: You can open and close chat with I

0 / 1000