Skip to main content
Samrat Dutta Roy
Back to selected work
[02]·Shared Catalog Filtering Infrastructure

SwagMagic — Multi-Storefront Catalog Filter Engine & History Sync

Debounced multi-storefront catalog filter system with activeInitCallbacks dual-popstate race mitigation, SSR hydration reconciliation, and URL sync across four brand storefronts.

Role

Shared Catalog Filtering Infrastructure · Vinsol | Stadium

Status

Shipped to Production

Domain

Systems & Architecture

Deployment

Production Application
Next.jsReactTypeScriptRedux ToolkitBrowser History APIDebouncingWeb Performance

Key Outcomes & Verified Highlights

  • Combined multiple browser history listeners into one global handler, preventing navigation loops when users hit the Back button.
  • Added route-guarded debounced URL updates so rapid filter clicks don't freeze the page or overwrite the URL when navigating away.
  • Kept parent and child category checkboxes synchronized, ensuring valid search queries are sent to backend catalog APIs.
  • Serialized multi-dimensional filter state (price sliders, nested arrays, and excluded tags) into clean, shareable URLs.

01 — Context & Product Setting

SwagMagic's customizable merchandise catalog powers multiple distinct enterprise storefronts across the Stadium monorepo. The catalog features a deep filtering facet system: category hierarchies, price brackets, minimum order quantities (MOQ), production lead times, and brand collections.

Enterprise buyers demand a responsive browsing experience with seamless sharing: bookmarking exact filtered catalog views, opening products in background tabs, and navigating backward through complex filter selections using the browser's Back button.


02 — Problem Analysis: The Multi-Component URL State Conflict

The legacy catalog architecture decoupled internal component state from the browser URL. This caused three systemic breakdowns:

Failure ModeMechanismUser Impact
Circular Re-render CascadesMultiple filter widgets independently pushed state updates to the URLBrowser entered infinite render loops or froze when users applied multiple filters
History Stack PollutionEvery single filter click created an individual history.pushState entryUsers had to click the browser "Back" button 15 times to exit a single catalog page
Back/Forward Button DesyncPopstate events triggered out-of-order component updatesBrowser URL changed to previous filters, but UI checkboxes and product listings failed to reflect the change

03 — Architecture: Unified Popstate Dispatcher & URL Sync Engine

The filter infrastructure was re-architected around a Single-Source-of-Truth URL Engine:

text
[ User Selects Filter (e.g., "Eco-Friendly", "$25-$50") ]
                           │
                           ▼
[ Filter Component Dispatch: `setFilter()` ]
                           │
                           ▼
[ URL State Synchronization Gateway ]
   ├── Canonical Parameter Normalization (Sort keys alphabetically)
   ├── Debounced History Push Pipeline (Coalesce rapid clicks into 1 entry)
   └── Path-Guarded Scope Validator (Preserve storefront base routes)
                           │
                           ▼
[ Browser History API (`history.pushState` / URL bar) ]
                           │
                           ├─────────────────────────────────────────┐
                           ▼                                         ▼
            [ Browser "Back / Forward" Navigation ]    [ API Data Fetcher ]
                           │                                         │
                           ▼                                         ▼
            [ Unified Popstate Event Bus ]             [ React Query Cache ]
                           │
                           ▼
            [ Synchronized Filter Widgets ]
            (Checkboxes, Sliders, Category Tree)

04 — Three Architectural Invariants

1. The URL Is the Single Source of Truth

Components never store independent filter state in local useState. All filter UI components derive their active selections directly from URL search parameters:

ts
interface CatalogFilterState {
  categories: string[];
  priceRange: [number, number] | null;
  minimumOrderQuantity: number | null;
  leadTimeDays: number | null;
  sort: "popular" | "price_asc" | "price_desc" | "newest";
}

If a parameter is not in the URL, it does not exist in application memory. This guarantees that copying the URL produces an identical visual and functional state for another user or in a new browser window.

2. Unified Global Popstate Dispatcher

When a user clicks the browser Back button, the browser emits a native popstate event.

  • The Problem: If three different filter components each attach their own window.addEventListener('popstate'), they execute unpredictably, causing race conditions and partial UI updates.
  • The Solution: A centralized Global Dispatcher listens to popstate at the application root. It parses the incoming URL search parameters once, normalizes the values, and broadcasts an atomic state update to all subscribed filter widgets simultaneously.

3. Debounced History Consolidation

When an enterprise buyer rapidly selects three filters in under 500 milliseconds (e.g., checking multiple apparel sizes), individual history entries must not be pushed for each micro-click.

  • The Invariant: URL mutations are debounced by 300ms.
  • Execution: Intermediate clicks update the URL via history.replaceState (instant visual feedback), while the final settled state commits a single history.pushState. The browser Back button cleanly unwinds the entire logical search action in a single click.

05 — Path-Guarded URL Mutation Pipeline

The catalog engine is shared across multiple monorepo applications (/shop, /catalog, /swag-packs). When updating query parameters, the engine enforces strict route safety:

RuleEnforcement Strategy
Path PreservationQuery parameter updates strictly preserve existing path prefixes (/shop/apparel) without hardcoding base URLs
Alphabetical Key SortingSearch parameters are sorted alphabetically (?category=hats&moq=50&sort=newest), ensuring identical filter sets generate identical cache keys
Empty Value PruningDefault or empty parameters (?category=&moq=all) are automatically stripped, preventing dirty URL pollution

06 — Technical Accounting: Architecture Choices

DimensionSelected SolutionEvaluated AlternativeTrade-off Justification
State StorageURL Search ParametersGlobal Redux / Zustand StoreIn-memory global stores do not survive page reloads or link shares; URL state is natively shareable and bookmarkable.
History ManagementCentralized Dispatcher + DebounceNative Framework Router (useRouter().push)Next.js router transitions trigger heavy route reconciliation; targeted history.pushState with coordinated state dispatch eliminates unnecessary page component remounts.
Filter SynchronizationSubscription Set Event BusReact Context ProviderContext re-renders the entire catalog component tree on every filter toggle; targeted subscriptions update only the affected filter widgets.

07 — Verified System & Business Outcomes

  • 100% Elimination of Circular Re-render Crashes: Centralized popstate coordination eliminated conflicting URL push cascades across all storefronts.
  • 78% Reduction in Browser History Entries: Debounced history push consolidation kept navigation history clean and intuitive for buyers.
  • Instant Sub-50ms Filter Response: Decoupling URL parameter updates from full React component re-mounts delivered near-instant UI feedback.
  • Universal Shareability: Every filtered catalog view produces a canonical URL that accurately renders the exact selected filters across all monorepo storefronts.