PLP Configuration Options
Configure the Fast Simon PLP widget by setting options on window.__fast_options before the widget loads.
Configuration Structure
Options are set globally on the window object:
window.__fast_options = {
// Product Display
with_product_attributes: true,
list_view_default_value: false,
numberOfColumns: { desktop: "4", tablet: "3", mobile: "2" },
// URL & Navigation
full_product_url_path_redirect: true,
url_parameters_to_keep: ["utm_source"],
// Filters
default_value_for_hide_filters_button: false,
disable_selected_filters_promote: false,
// Swatches & Variants
color_attribute_names: ["Color", "colour"],
hide_oos_swatches: false,
swatch_width: 32,
// SPA
spa_dynamic_sections: { "header": "before" },
// Advanced
disable_shadow_root: false,
};
Available Options
Product Display
| Option | Type | Default | Description |
|---|---|---|---|
with_product_attributes | boolean | false | Include product attributes in results |
list_view_default_value | boolean | false | Set list view as the default instead of grid view |
numberOfColumns | Record<string, string> | - | Number of columns per device for grid view (e.g. { desktop: "4", tablet: "3", mobile: "2" }) |
numberOfColumnsList | Record<string, string> | - | Number of columns per device for list view |
custom_missing_product_img | string | - | URL for a custom fallback image when a product image is missing |
bring_product_pdp_images | boolean | false | Add product images from PDP into the grid hook products data (pdpImages param) |
show_carousel_marks_not_just_on_hover | boolean | false | Show image carousel dot indicators permanently, not only on hover |
show_quick_view_button_always | boolean | false | Keep the Quick View button permanently visible on desktop product cards instead of only on hover. Mobile/tablet behavior is unchanged |
show_quick_add_button_always | boolean | false | Keep the variant Quick Add permanently visible on desktop product cards instead of only on hover. Covers both the standalone on-image variant selector and the combined Quick Add / Quick View button bar — when the bar is in use, the whole bar stays visible. Mobile/tablet behavior is unchanged, and the size options still open on click rather than showing expanded |
quick_view_hide_single_value_options | boolean | false | When true, variant selector options with only a single value are hidden in the Quick View modal (e.g. a product with one size will not show the size selector) |
max_carousel_images | number | 6 | Maximum number of images in the product image carousel (includes the main image). Applies to both auto and arrows carousel types |
omit_carousel_image_indexes | number[] | - | 0-based indexes into the product's PDP image list to drop from the tile carousel — e.g. [1] removes the second PDP image from every product. Useful when an image is already shown elsewhere on the card (such as a theme's hover overlay) and would otherwise appear twice. Applied before max_carousel_images, so omitting an index does not cost you a slide. The main product image is never affected. Note it also changes which image the 2nd-image carousel type shows on hover, since that picks from the same list |
product_image_custom_class | string | - | Extra class(es) added to every product image in the grid — carousel images, hover second-image, pair-mode images and plain tiles. Space-separate for more than one. See Styling with a custom image class for the shadow-root and specificity caveats |
URL & Navigation
| Option | Type | Default | Description |
|---|---|---|---|
full_product_url_path_redirect | boolean | true | When true, product URLs include the /collections/collectionName segment. When false, URLs use /products/product-name directly. Shopify only |
url_parameters_to_keep | string[] | [] | URL parameters to preserve when navigating between pages (e.g. active filters or UTM tags) |
pagination_querystring_parameter | string | - | Custom query string parameter name used for pagination |
disable_pagination_scroll_to_top | boolean | false | When true, prevents the page from scrolling to the top when clicking pagination controls |
disable_pdp_redirect | boolean | false | (SSR only) Disables automatic redirect to the PDP when clicking a product. Useful for custom routing solutions |
disable_navigate_to_category | boolean | false | Hard-disable the window.SerpOptions.navigateToCategory() API for this site. When true, every call returns { ok: false, error: "..." } with guard "merchant-disabled". See Logic / SerpOptions Methods and the Facet Combo → Category Redirect example |
max_navigate_to_category_per_page | number | 5 | Maximum number of navigateToCategory() calls allowed per page-load. The counter resets when the shopper hits browser back/forward. Lower values clamp aggressive merchant snippets; higher values allow deeper exploration chains |
Filters
| Option | Type | Default | Description |
|---|---|---|---|
disable_selected_filters_promote | boolean | false | Disable promotion of active filters to the top of the filter list |
default_value_for_hide_filters_button | boolean | false | When true, filters are hidden on page load and users must click to reveal them |
disable_auto_close_filters_when_click_outside | boolean | false | Prevent the filter panel from automatically closing when clicking outside it |
parse_sub_category_tag_filter | (name: string) => string | - | Custom function to parse subcategory tag filter names. Shopify only |
disable_auto_collapsed_filters | boolean | false | Disable auto-collapsed state for filters (applies only when filter layout is set to collapsed) |
category_filter_as_regular_facet | boolean | false | Display the category filter as a regular facet instead of a dedicated category widget |
persist_filters_visibility | boolean | false | (SSR only) Preserve the filter sidebar's open/closed state across filter selections and page changes |
Swatches & Variants
| Option | Type | Default | Description |
|---|---|---|---|
color_attribute_names | string[] | ["Color"] | All possible color attribute names to recognize (e.g. ["Color", "colour", "Couleur"]) |
swatch_width | number | - | Width of swatch elements in pixels |
hide_oos_swatches | boolean | false | When true, removes sold-out color swatches from the product card. It only ever hides — it never adds or changes a marker on sold-out swatches, and setting it to false (already the default) does nothing. It also applies to one swatch source only: color options coming from alternative-color products that carry variant data. Swatches built from the product's own variants are not filtered by it. Not supported on V-Next SSR — the option is ignored there. To style sold-out swatches rather than hide them, see Out-of-Stock Color Swatches |
mark_oos_alt_swatches | boolean | false | When true, sold-out alternative-color swatches — color options that link out to a separate product — are marked like any other sold-out swatch (fs-swatch-out-of-stock plus the ban icon). They render as in stock by default, so leaving this off preserves current behavior. Availability comes from the linked product's own stock status; where the response carries no data for a linked product the swatch still renders as in stock. Not needed on V-Next SSR, which already marks them. See Out-of-Stock Color Swatches |
size_attribute_names | string[] | - | All possible size attribute names to recognize |
variant_size_custom_sort_order | string[] | - | Custom sort order for size variants (all values must be lowercase) |
get_selected_variant | (productId: string) => SelectedVariant \| undefined | - | Custom function returning variant data (image, SKU, price, etc.) for a given product ID |
add_current_product_as_first_variant | boolean | false | Add the current product using its first variant ID |
Collections
| Option | Type | Default | Description |
|---|---|---|---|
relatedCollectionsConfiguration | Record<string, string[]> | - | Maps each collection ID to an array of related collection IDs to suggest alongside it |
onlyServerRelatedCollections | boolean | false | Show only related collections returned from the server |
Layout & Responsive
| Option | Type | Default | Description |
|---|---|---|---|
serp_media_queries | {device: string, query: string}[] | - | Override default screen breakpoints (e.g. [{device: 'mobile', query: '(max-width: 480px)'}]) |
custom_top_scroll_selector | string | - | CSS selector for the element the page scrolls to on pagination |
modern_mobile_filters_animation | AnimationOptions | - | Animation configuration for the mobile filter panel |
apply_narrow_manually | boolean | false | Apply filter/narrow actions manually instead of automatically |
SPA (Single Page Application)
| Option | Type | Default | Description |
|---|---|---|---|
spa_disable_auto_close_mobile_menu | boolean | false | Disable automatic closing of the mobile menu during SPA navigation |
spa_dynamic_sections | Record<string, 'before' \| 'after'> | - | Page sections to re-render during SPA navigation, keyed by CSS selector |
before_spa_navigation_callback | (event?: Event) => void | - | Callback invoked before each SPA navigation |
after_spa_navigation_callback | (event?: Event) => void | - | Callback invoked after each SPA navigation |
Custom Functions
| Option | Type | Description |
|---|---|---|
parseProductTitle | (title: string, productData: Product, selectedVariant: Variant) => string | Transform product titles before display. Receives the title, full product object, and selected variant |
parseProductPrice | (formattedPrice: string, comparePrice: string, productData?: Product, altProductData?: AlternativeProduct) => string | Transform product prices before display. Also receives optional product and alternative (swatch) product data |
parseComparePrice | (formattedCompare: string, productData?: Product, altProductData?: AlternativeProduct) => string | Transform compare-at (strike-through) prices before display |
parsePageTitle | (title: string, pageType: 'collection' \| 'search' \| 'landing', data: {categoryName?: string, searchTerm?: string, resultsFor?: string, didYouMean?: string[]}) => string | Transform the page H1 title before display |
viewItemListCallback | (event_items?: {items: GTAGItem[], category: 'filter' \| undefined}) => void | Called after the view_item_list analytics event fires |
Advanced
| Option | Type | Default | Description |
|---|---|---|---|
disable_shadow_root | boolean | false | Disable Shadow DOM encapsulation. When enabled, styles are injected into document.head, allowing global CSS to affect the widget |
avoid_bigcommerce_jwt_logged_on_user_check | boolean | false | Skip JWT token verification for logged-in BigCommerce users |
use_bigcommerce_jscontext_customer_group_id | boolean | false | BigCommerce B2B: read the live customer_group_id from the storefront Stencil context (window.jsContext.customer.customer_group_id) instead of the app JWT. Required for correct group-based pricing when a B2B user masquerades between companies, since the JWT is not refreshed on company switch |
companyLocationProvider | () => string \| number \| undefined | none | Shopify B2B: a callback returning the buyer's company location id, sent as company_location_context. Required for B2B catalog visibility: without it the buyer is evaluated as an ordinary shopper in their market, so products restricted to their B2B catalog are hidden from them. Called on every request rather than once at init, so a mid-session location switch is picked up. When configured it is authoritative, so return undefined for a shopper who is not buying on behalf of a company |
Shopify B2B: supplying the company location
On a store that restricts products to a B2B catalog, Fast Simon needs the buyer's company location to resolve which catalogs they are entitled to. Without it the buyer is evaluated as an ordinary shopper in their market, and the products restricted to their catalog are filtered out of their results.
Shopify exposes the buyer's active location to Liquid as
customer.current_location, a
company_location object. Render its
id into the page and return it from the provider:
<script>
var __fast_options = __fast_options || {};
{%- comment -%} Assigned unconditionally: on a theme that navigates client-side, leaving a
previous page's value in place would keep sending a location the shopper no longer has. {%- endcomment -%}
window.fsCompanyLocation = {% if customer and customer.b2b? and customer.current_location %}{{ customer.current_location.id }}{% else %}undefined{% endif %};
__fast_options.companyLocationProvider = function () {
return window.fsCompanyLocation;
};
</script>
If your bundle predates companyLocationProvider
Set the value directly on window.__FAST_CUSTOMER_DETAILS instead. Every Fast Simon storefront
bundle reads it, including versions released before the provider existed, so this works today on any
bundle you already have:
{% if customer and customer.b2b? and customer.current_location %}
<script>
window.__FAST_CUSTOMER_DETAILS = {
customer_current_location: "{{ customer.current_location.id }}"
};
</script>
{% endif %}
Place it above the Fast Simon script tag and render it per page, for the same reasons as above. Where both are present the provider wins, so you can add the provider later without removing this first.
Liquid renders the location id as a number, so the provider example above hands back a number
while the __FAST_CUSTOMER_DETAILS example quotes it and hands back a string. Both are accepted, and
so is the gid://shopify/CompanyLocation/... string form you get if you source the location from the
Storefront or Admin GraphQL API instead. Fast Simon trims the value and sends it as-is, so pick
whichever is convenient in your theme.
Two behaviors worth knowing:
- The provider is called on every request, not once at page load. A buyer who switches location mid-session is picked up as soon as your code updates the value, with no reload.
- While a provider is configured it is authoritative. Return
undefinedfor visitors who are not buying on behalf of a company; Fast Simon will not substitute a location for them.
GTAGItem Interface
export interface GTAGItem {
item_list_id: string; // category id
item_list_name: string; // category name
item_id: string | number; // product id
item_name: string; // product name
item_brand: string;
currency: string;
price: number;
index: number;
}
How to add custom options:
Add the __fast_options variable to your HTML head within a script tag, for example:
<script>
var __fast_options = __fast_options || {};
__fast_options.with_product_attributes = true;
__fast_options.relatedCollectionsConfiguration = {
'collectionId1': ['relatedCollectionId1', 'relatedCollectionId2', 'relatedCollectionId3'],
'collectionId2': ['relatedCollectionId4', 'relatedCollectionId5', 'relatedCollectionId6'],
'collectionId3': ['relatedCollectionId1', 'relatedCollectionId2', 'relatedCollectionId4']
};
__fast_options.serp_media_queries=[
{device: 'mobile', query: '(max-width: 480px)'},
{device: 'tablet', query: '(min-width: 481px) and (max-width: 820px)'},
{device: 'desktop', query: '(min-width: 821px)'}
];
__fast_options.variant_size_custom_sort_order = ['one size', 'xs', 'small', 'medium', 'large', 'xl', '4x',
'5x', '6x', '0', '1', '1x', '1x (16-18)', '2', '2x', '2x (18-20)', '3', '3x', '3x (22)', '5', '5.5', '6',
'6.5', '7', '7.5', '8', '8.5', '9', '10', '11', '13', '13/14', '15', '15/16','34b','34c','34d','34dd (e)',
'34dd(e)','34ddd (f)','36c','36d','36dd (e)','36ddd (f)','38c','38d','38dd','38dd (e)','38ddd','38ddd (f)',
'40c','40d','40dd','40dd (e)','40ddd','40ddd (f)','40g','42d','42dd (e)','42ddd','42ddd (f)','42g','44c',
'44d','44dd (e)','44ddd','44ddd (f)','44f','44g','46d','46dd (e)','46ddd','46ddd (f)','46f','46g'];
__fast_options.numberOfColumns = {
'desktop': "3",
'tablet': "3",
'mobile': "2"
};
__fast_options.numberOfColumnsList = {
'desktop': "2",
'tablet': "2",
'mobile': "1"
};
__fast_options.parseProductTitle = function(title, productData, selectedVariant) {
return title.replace("/"," ");
};
__fast_options.parsePageTitle = function(title, pageType, data) {
if (pageType === 'collection') {
return 'Shop ' + data.categoryName;
}
if (pageType === 'search') {
return 'Showing results for ' + data.searchTerm;
}
if (pageType === 'landing') {
return data.categoryName;
}
return title;
};
</script>