Category Filters
Filter pills used on list pages. Desktop wraps onto multiple lines; mobile uses a horizontal scroll with a right-side gradient fade. Click writes to the `?category=` URL param with scroll preservation.
Blog filters
Active category
Case study filters
Props
| Prop | Type | Default | Description |
|---|---|---|---|
categories* | string[] | - | The filter options. The component prepends the "All" pill automatically. |
activeCategory | string | null | - | Overrides the active category from context. Only for rendering a fixed state in isolation (like the examples on this page) — pages should let the provider drive it. |
resultCount | number | - | Number of items currently shown — rendered below the filter row. Omit to derive it from counts/totalCount. |
allLabel | string | "All" | Label used for the reset pill. |
Import
tsx
import {
CategoryFilterProvider,
CategoryFilters,
} from '@/components/ui/CategoryFilters'Usage
tsx
// Wrap the pills and the list they filter in one provider. It owns
// the active category and keeps ?category= in the URL for shareability.
//
// It deliberately does NOT use useSearchParams(): calling that in a
// statically-rendered route makes Next skip prerendering the subtree,
// which strips every item link out of the static HTML and hides them
// from search crawlers. State starts null so the full list prerenders.
<CategoryFilterProvider basePath="/work">
<CategoryFilters
categories={['Web App', 'Website', 'Mobile App']}
totalCount={caseStudies.length}
counts={categoryCounts}
/>
<WorkCarousel caseStudies={caseStudies} />
</CategoryFilterProvider>