A small client-side product discovery app built with Next.js App Router,
TypeScript, and Tailwind CSS. It loads the provided home goods catalog from
public/items.json, then lets users search, browse, filter, sort, and inspect
products without a backend.
- Client-side catalog loading for about 4,000 home goods products
- 300ms debounced search across product title, brand, and tags
- Category, rating, price range, and in-stock filters with an explicit Apply action
- Responsive product grid with lazy images and infinite loading
- Product detail modal with larger image, metadata, tags, description, and Escape-to-close behavior
- Recently viewed products stored in
localStorage - URL query sync for search, applied filters, and sort
- Skeleton loading states for initial catalog load and infinite loading
- Defensive normalization for real dataset quirks like
nulldescriptions, missing ratings, string prices, and missing images
Search intentionally stays simple and fast. A query is split into keywords and matched against three fields:
- Title match: 100 points
- Brand match: 50 points
- Tag match: 25 points
Default sorting uses relevance first, then rating as a tie-breaker. This favors products whose names directly match what the user typed, then brand matches, then broader tag matches.
Descriptions are not searched by default. After inspecting the dataset, the descriptions looked repetitive and lower-signal than titles, brands, and tags. Including them would make broad searches noisier without helping discovery much.
The catalog is small enough to scan in memory. The app uses useMemo, a 300ms
debounce, memoized product cards, and infinite loading so typing does not
rerender thousands of cards at once.
The source URL does not send browser CORS headers, so the JSON is copied into
public/items.json and fetched as a same-origin static asset. Remote product
images are allowed through next.config.ts.
The current search favors simplicity and speed over advanced fuzzy matching. In a larger catalog, a dedicated search engine or indexed client search could be considered. The current infinite loading also keeps already-loaded cards in the DOM; if the catalog grew far beyond this size, virtualization would be the next performance step.
- Fuzzy matching for misspellings and plurals
- Better keyboard focus trapping inside the modal
- Filter count chips and clearer applied-filter summaries
- Product image fallback assets instead of plain placeholders
- Virtualized grid if the catalog grows significantly
npm install
npm run devOpen http://localhost:3000.
npm run lint
npm run build