Skip to main content

Strategies

URL-based state management strategies for filters, pagination, and product variant selection in e-commerce applications.

Purpose

This library provides configurable strategies for managing URL state synchronization with filters, pagination, and product variant selection in e-commerce applications. It enables deep linking, bookmarkable URLs, and browser history management for product listings and search results. The Strategy pattern implementation allows you to customize how filter and pagination state is stored and retrieved, with default URL-based implementations provided out of the box.

Features

  • URL Synchronization: Automatic sync between component state and URL parameters
  • Deep Linking: Shareable URLs that preserve filter, pagination, and product variant selection state
  • Browser History: Proper back/forward navigation support using replaceState
  • SSR Support: Graceful handling of server-side rendering (no-op when window is undefined)
  • Customizable: Implement custom strategies by implementing the strategy interfaces
  • Platform Agnostic: Works on web, mobile, and SSR environments
  • Performance Optimized: Minimal overhead with efficient URL updates

Installation

npm install @haus-storefront-react/strategies

Note: This is not a public package. Contact the Haus Tech Team for access.

API Reference

DefaultFilterUrlStrategy

Manages filter state in URL parameters using comma-separated values. Implements the FiltersStoreStrategy interface to synchronize filter state with URL query parameters. Returns empty object when window is undefined (SSR environments).

Methods

MethodSignatureDescription
getInitialFilters() => ActiveFilters | undefinedRetrieves active filters from URL query parameters. Parses comma-separated values from URL params into an ActiveFilters object. Returns empty object {} in SSR environments.
setActiveFilters(filters: ActiveFilters) => voidUpdates URL query parameters with active filters. Converts filter arrays to comma-separated values. Filters out empty values and updates browser history using replaceState. No-op in SSR environments.

DefaultPaginationUrlStrategy

Manages pagination state in URL parameters using a page query parameter. Implements the PaginationStoreStrategy interface to synchronize current page with URL. Returns 1 when window is undefined or when no page parameter exists.

Methods

MethodSignatureDescription
getInitialPage() => numberRetrieves the current page number from URL query parameters. Parses the page parameter from URL. Returns 1 if parameter is missing, invalid, or in SSR environments.
setPage(page: number) => voidUpdates URL query parameters with the current page number. Sets page parameter when page > 1, removes parameter when page === 1. Updates browser history using replaceState. No-op in SSR environments.

ProductVariantStoreStrategy / DefaultProductVariantUrlStrategy

ProductVariantStoreStrategy is the storage-agnostic interface for reading and persisting selected variant options (option group code → option ID). Implementations can sync to URL, Redux, or any other store. DefaultProductVariantUrlStrategy is the URL-based implementation: it uses one query parameter per option group (param name = option group code, value = option ID), e.g. ?size=opt-1&color=opt-2. Returns an empty object when window is undefined (SSR). Invalid or unknown option IDs are ignored by the consumer (product variant selector).

When used with the product variant selector, the URL is only updated after the user has actively changed a variant (e.g. by clicking an option). The initial or default selection on first page load is not written to the URL; once the user has interacted, subsequent changes (including switching back to the default variant) are synced.

Methods

MethodSignatureDescription
getInitialOptions() => SelectedVariantOptionsReturns the current selected options from the store. Consumer uses only keys that match product option group codes.
setOptions(options: SelectedVariantOptions, optionGroupCodes: string[]) => voidPersists the selected options. Clears existing values for optionGroupCodes first, then sets the new values. For the URL implementation: preserves pathname, other params, and hash. No-op in SSR.

Basic Usage

Simple Filter Management

import React, { useState, useEffect } from 'react'
import { DefaultFilterUrlStrategy } from '@haus-storefront-react/strategies'
import { ActiveFilters } from '@haus-storefront-react/shared-types'

function ProductFilters() {
const [filters, setFilters] = useState<ActiveFilters>({})
const filterStrategy = new DefaultFilterUrlStrategy()

useEffect(() => {
// Initialize filters from URL
const initialFilters = filterStrategy.getInitialFilters()
setFilters(initialFilters || {})
}, [])

const updateFilters = (newFilters: ActiveFilters) => {
setFilters(newFilters)
filterStrategy.setActiveFilters(newFilters)
}

const handleBrandChange = (brands: string[]) => {
updateFilters({
...filters,
brand: brands,
})
}

return (
<div className='filters'>
<h3>Filters</h3>

<div className='filter-group'>
<label>Brand</label>
<select
multiple
value={filters.brand || []}
onChange={(e) => {
const selected = Array.from(
e.target.selectedOptions,
(option) => option.value,
)
handleBrandChange(selected)
}}
>
<option value='apple'>Apple</option>
<option value='samsung'>Samsung</option>
<option value='google'>Google</option>
</select>
</div>
</div>
)
}

Simple Pagination Management

import React, { useState, useEffect } from 'react'
import { DefaultPaginationUrlStrategy } from '@haus-storefront-react/strategies'

function ProductPagination({ totalPages }: { totalPages: number }) {
const [currentPage, setCurrentPage] = useState(1)
const paginationStrategy = new DefaultPaginationUrlStrategy()

useEffect(() => {
// Initialize page from URL
const initialPage = paginationStrategy.getInitialPage()
setCurrentPage(initialPage)
}, [])

const handlePageChange = (page: number) => {
setCurrentPage(page)
paginationStrategy.setPage(page)
}

return (
<div className='pagination'>
<button
disabled={currentPage <= 1}
onClick={() => handlePageChange(currentPage - 1)}
>
Previous
</button>

<span>
Page {currentPage} of {totalPages}
</span>

<button
disabled={currentPage >= totalPages}
onClick={() => handlePageChange(currentPage + 1)}
>
Next
</button>
</div>
)
}

Advanced Usage

Extending Default Strategies

import { DefaultFilterUrlStrategy } from '@haus-storefront-react/strategies'
import { ActiveFilters } from '@haus-storefront-react/shared-types'

class AdvancedFilterStrategy extends DefaultFilterUrlStrategy {
setActiveFilters = (filters: ActiveFilters): void => {
// Call parent implementation
super.setActiveFilters(filters)

// Additional custom logic
this.trackFilterChanges(filters)
this.updatePageTitle(filters)
}

private trackFilterChanges = (filters: ActiveFilters): void => {
// Track filter changes for analytics
const filterCount = Object.values(filters).reduce(
(total, arr) => total + (arr?.length || 0),
0,
)

if (typeof gtag !== 'undefined') {
gtag('event', 'filter_applied', {
filter_count: filterCount,
filters: Object.keys(filters),
})
}
}

private updatePageTitle = (filters: ActiveFilters): void => {
// Update page title based on active filters
const filterNames = Object.keys(filters)
if (filterNames.length > 0) {
document.title = `Products - Filtered by ${filterNames.join(', ')}`
} else {
document.title = 'Products'
}
}
}

Made with ❤️ by Haus Tech Team