Skip to main content

Configuring JS layout

Fast Simon PLP uses advanced view rendering to bring the most optimized user experience. To adjust elements or logic we provide unique hooks you can use to edit the elements we generate.

Getting Started

PLP hook Flow

  1. Register a hook using a script tag located in the <head>
  2. PLP will pickup the hook and will execute any code you write there
  3. Hook will run again on every life cycle update

Rules

  1. Registering a hook with the same event, ensure they do not modify the same code scope to avoid conflicts.
  2. You can only manipulate the dom element provided by the hook.
  3. We only update the dom nodes that changed in each life cycle change, so make sure your code doesn't add the same elements twice (see examples)
  4. hook runs per item, data will contain the specific identifier

Register a new hook & execute it

<script>
function hooks() {
window.SerpOptions.registerHook('{{ customization_hook_name_1 }}', ({data, element}) => {
// logic here
});

window.SerpOptions.registerHook('{{ customization_hook_name_2 }}', ({data, element}) => {
// logic here
});
}

// execution here
if (window.SerpOptions) {
hooks();
} else {
window.addEventListener('fast-serp-ready', function () {
hooks();
});
}
</script>

Available Hooks

Event NameDescriptionHook ParamsReturn Params
"serp-product-grid"Life cycle hook: every plp product grid updateobject: {products: Products, element: HTMLElement}void
"serp-filters"Life cycle hook: every plp filters updateobject: {facets: Facet[], element: HTMLElement}void
"serp-quick-view"Life cycle hook: every plp quick view pop up createdobject: {product: Product, index: number, onClose: (reason: CloseQuickViewReason, event: [Event]) => void, mode: [Device], element: HTMLElement}void
"format-serp-size-variant-selector-options"Format size selector options (display text only)stringstring
"serp-fixed-narrow"Life cycle hook: add a fixed narrowvoidHookNarrow
"serp-product-swatches"Life cycle hook: every plp product swatches updateobject: {colors: [colors[]], swatchClick: [Event], showMoreClick: boolean, element: HTMLElement}void
"serp-removable-tags"Life cycle hook: every plp removable tag filter updateobject: {tags: [RemovableTags], element: HTMLElement}void
"serp-top-page"Life cycle hook: every plp top page updatevoidvoid
"serp-main-block"Life cycle hook: every plp main block updatevoidvoid
"serp-paging"Life cycle hook: every pagination update (SSR version only)voidvoid

type references: Typescript Types

HookNarrow.ts
interface HookNarrow{
[FilterName: string]: FilterValues[]
}

type FilterValues = string

SerpOptions Methods

In addition to registerHook, window.SerpOptions exposes the following helper methods you can call from your customization code:

MethodDescriptionReturns
getNarrow()Returns the current narrow (selected filters) as Record<string, Set<string>>Narrow
getQuery()Returns the current search query (or empty string if not in search mode)string
getSort()Returns the current sort keystring
getPage()Returns the current page numbernumber
getCategory()Returns the current category data (async)Promise<Category \| null>
navigateToCategory(args)Programmatically swaps the current collection for a different one. See below.{ ok: true } \| { ok: false, error: string }

Tells the storefront to navigate to a different collection — using the same categories_navigation request the shopper would trigger by clicking a link to that collection. Use this from a custom event listener to redirect on specific filter combinations or other shopper signals.

navigateToCategory signature
interface NavigateToCategoryArgs {
categoryID: string; // required, Fast Simon category id
categoryURL?: string; // strongly recommended; falls back to `/collections/${categoryID}` (Shopify path style) if omitted
clearNarrow?: boolean; // default true; if false, residualNarrow replaces the cleared narrow
residualNarrow?: Record<string, Set<string> | string[]>;
reason?: string; // free-form analytics tag, echoed back on the navigate event
}

type NavigateToCategoryResult =
| { ok: true }
| { ok: false, error: string };

Cross-platform note: the categoryURL fallback uses Shopify's /collections/... path convention. On BigCommerce, Magento, Wix, or custom storefronts, always pass categoryURL explicitly with the path your platform produces (e.g. "/blue-large-shirts/" on BigCommerce, "/blue-large-shirts.html" on Magento). The categoryID itself comes from the Fast Simon dashboard and is identical regardless of platform.

Built-in safety guards

The function is safe to call from any listener — it has cooldowns and limits that prevent runaway calls. Each guard rejects with { ok: false, error: string } and dispatches a fs-custom-events-navigate-to-category-skipped event with the guard label:

GuardWhen it rejects
validationcategoryID missing or not a non-empty string
first-loadStorefront not yet hydrated (the canonical fast-serp-ready event hasn't fired)
search-modeA search query is active (feature is collection-only)
merch-rules-preview / merch-strategies-preview / preview-modeMerchandising or A/B preview is active
merchant-disabled__fast_options.disable_navigate_to_category is true
popstate-cooldownBrowser back/forward fired in the last 500 ms
cooldownAnother navigateToCategory call succeeded in the last 750 ms
in-flightA previous call is still navigating
max-per-pageExceeded __fast_options.max_navigate_to_category_per_page (default 5)
no-opargs.categoryID equals the current category id

The callCount resets when the shopper hits browser back/forward, so legitimate exploration isn't blocked.

Companion events

  • fs-custom-events-navigate-to-category — fires on a successful call. Detail: {from, to, reason}.
  • fs-custom-events-navigate-to-category-skipped — fires when a guard rejects. Detail: {error, guard, reason}.

See Custom Events for full payload references.

Example

Programmatic redirect
function start() {
window.SerpOptions.navigateToCategory({
categoryID: "287654321098",
categoryURL: "/collections/blue-large-shirts",
clearNarrow: true,
reason: "facet-combo:color:blue|size:large"
});
}

if (window.SerpOptions) {
start();
} else {
window.addEventListener('fast-serp-ready', start, { once: true });
}

A complete working example that listens to filter clicks and redirects on specific combos: Facet Combo → Category Redirect.