Product Collection
Sticky filter sidebar, sort toolbar and product grid — with the facets dropped rather than stacked on mobile, so a shopping page does not open as a form.
What's included
app/collection-page.tsx- 4 blocks it is built from, installed with it
- Needs lucide-react
Works with
- React
- Next.js
- Tailwind CSS
- TypeScript
npx hoverlab add collection-page
Or over MCP, from your editor's agent — no account needed.
License
Free to read, copy and install, for personal and non-commercial projects. Shipping it in client work or a paid product needs Pro ($79 once). The source lands in your repo and stops being ours — no attribution, nothing to upgrade.
Preview
Autumn / Winter
Knitwear
Fully fashioned merino and lambswool, knitted in Scotland. Made to keep its shape rather than to hit a price.
114 products
The real page, rendered in your current theme — every section below is a live block, not a screenshot.
Built from
All blocks- Faceted Filter SidebarCategory, size, colour and price facets as proper fieldsets, with colour swatches that carry their name as well as their hue.Product Listings
- Collection Sort ToolbarResult count, sort and density switch — with the count in an aria-live region, since it is the only feedback a filter change gives some users.Product Listings
- Product Card GridResponsive product cards with sale badges, ratings and a wishlist button layered above the card link so it stays clickable.Product Listings
- Scroll-Snap Product RailA "you might also like" row on native scroll-snap — touch swiping, momentum and keyboard support for free, with no carousel library.Product Listings
Only want one section? Open it and copy that block instead — browse Product Listings.
Shipped in these templates
Want the whole project rather than this one screen? A template ships every route, the theme tokens and the layout.
Source
/**
* A product collection screen — filters, toolbar, grid.
*
* The layout decision that matters: the filter sidebar is `hidden lg:block`
* and the toolbar carries its own Filters button below that width. Stacking
* a dozen facets above the products on a phone means the first thing a
* shopper sees on a shopping page is a form.
*
* On desktop the sidebar is `sticky` so it stays in reach while the grid
* scrolls. A filter panel that scrolls away at product forty is a filter
* panel nobody uses twice.
*/
import * as React from 'react'
import { ProductFilterSidebar } from '@/components/product-filter-sidebar'
import { CollectionToolbar } from '@/components/collection-toolbar'
import { ProductGrid } from '@/components/product-grid'
import { ProductRail } from '@/components/product-rail'
export default function CollectionPage() {
return (
<main className="min-h-screen bg-background text-foreground">
<div className="mx-auto w-full max-w-7xl px-4 py-10 sm:px-6 lg:px-8">
<header className="mb-8 max-w-2xl">
<p className="text-xs font-bold uppercase tracking-wider text-muted-foreground">
Autumn / Winter
</p>
<h1 className="mt-2 text-4xl font-extrabold tracking-tight">Knitwear</h1>
<p className="mt-3 text-pretty text-muted-foreground">
Fully fashioned merino and lambswool, knitted in Scotland. Made to
keep its shape rather than to hit a price.
</p>
</header>
<div className="flex gap-10">
{/* Sticky on desktop; below lg the toolbar's own button opens it. */}
<div className="hidden w-60 shrink-0 lg:block">
<div className="sticky top-6">
<ProductFilterSidebar />
</div>
</div>
<div className="min-w-0 flex-1">
<CollectionToolbar />
<div className="mt-6">
<ProductGrid />
</div>
<nav aria-label="Pagination" className="mt-10 flex justify-center">
<button
type="button"
className="rounded-xl border border-border/60 bg-background px-6 py-2.5 text-sm font-medium transition-colors hover:bg-muted"
>
Load more
</button>
</nav>
</div>
</div>
<div className="mt-16 border-t border-border/60 pt-10">
<ProductRail heading="Recently viewed" viewAllHref="/history" />
</div>
</div>
</main>
)
}
How to use it
- 1. Copy each of the 4 blocks above into
components/— each block page has its own copy button. - 2. Drop this file at
app/collection-page.tsx. The imports already point at@/components/…, so they resolve with no edits. - 3. Delete the sections you do not want. Every block takes props, so the copy changes without the layout moving.
For AI
The component, its props, the design tokens it expects and the command that installs it — as one prompt. Paste it into Claude, Cursor, v0 or ChatGPT and what they build around it will match the rest of the catalog instead of inventing its own system.
See the prompt
More Commerce Pages
Product Detail
Gallery, sticky buy box, details accordion and reviews — ordered by what a shopper needs before they can decide.
Shopping Bag
Editable lines beside a sticky summary, so the total and the checkout button survive a scroll through four quantity changes.
Checkout
Address and payment with the site navigation stripped and the order summary moved above the form on mobile — both aimed squarely at completion rate.
Order Confirmed
The post-purchase screen, with navigation restored and a rail that shows what to buy next rather than what was just bought.
Account Orders
Order history in the settings shell rather than a second sidebar layout — an account area and a settings area are the same shape.
Product Comparison
The page between a listing and a product detail, with the two questions a comparison table alone cannot answer: will it fit, and can I actually have it.
Express Checkout
Checkout for the buyer who will not fill in a form: wallet buttons above the fields, and the address a wallet returns shown and changeable before the charge rather than after it.
Order Tracking
For the customer with an order number and no account — a lookup, an arrival range rather than a promised date, and the review form where the "how did we do" email actually lands.