Product search
Search and filter Experience products, returning one result per product.
The product_search tag searches a creator's Experience products and returns one result per product.
This is useful for building search and listing pages where each product appears once, regardless of how many departure dates it has. To return a separate result for each departure date, use the experience_slot_search tag instead.
The search only returns public, published products. Products are excluded if:
- The Experience is unpublished.
- The Experience has been marked as private in Manage product availability.
Only one product_search tag should be used per page to ensure expected results.
Basic usage
{% product_search
name: "Beyond",
duration: { greater_than: 3, less_than: 8 },
page_size: 12,
sort: "departure_date_asc"
%}
{% for item in result.items %}
<p>{{ item.name }}</p>
{% endfor %}
<div>
{{ paginate.collection_size }} results
{{ paginate | default_pagination }}
</div>
{% endproduct_search %}The tag makes the following objects available:
| Object | Description |
|---|---|
result.items | Array of matching Products. |
paginate | Paginate object for paginated results. |
available_months | Array of Date objects representing months that contain available departures. |
The Search object is also available when the page URL contains ?search[...] query parameters. It reflects the applied URL parameters and is useful for preserving filter state in forms.
Using query parameters
Search parameters can be passed in the URL using the search query parameter:
/search?search[name]=beyond&search[departure_date][greater_than]=2024-11-22&search[sort]=departure_date_ascQuery parameters override any matching attributes passed directly to the tag. This allows you to set defaults in the tag and let URL parameters refine the search.
page_size cannot be set via URL — it is a tag-only attribute.
Parameters
| Parameter | Type | Description |
|---|---|---|
name | String | Partial text search on product name (case-insensitive). |
category | String or array | Filter by product category (case-sensitive). |
subcategory | String or array | Filter by product subcategory (case-sensitive). |
city | String or array | Filter by location city (case-insensitive). |
country | String or array | Filter by country code or name. |
departure_date | Range hash | Filter by departure date. |
departure_month | Integer or string | Filter by month across all years. |
duration | Range hash | Filter by experience duration. |
product_id | UUID or array | Restrict to specific experience(s). |
series_id | UUID | Filter by series. |
exclude_sold_out_products | Boolean | Exclude sold-out products. |
active_promotion | Boolean | Filter by promotion status. |
include_organisation_products | Boolean | Search across the organisation. |
sort | String | Sort order. Default: "departure_date_asc". |
page_size | Integer | Results per page. Default: 10. Tag only. |
| Tag categories | URL only | Filter by company-defined tag categories. |
name
Partial text search on the experience name. Case-insensitive.
{% product_search name: "my experience" %}
{% endproduct_search %}URL: ?search[name]=my+experience
category
Filter by experience category. Case-sensitive. Accepts a single value or an array.
{% product_search category: "Active" %}
{% endproduct_search %}
{% product_search category: ["Active", "Adventure"] %}
{% endproduct_search %}URL: ?search[category]=Active
subcategory
Filter by experience subcategory. Case-sensitive. Accepts a single value or an array.
{% product_search subcategory: "Wellness" %}
{% endproduct_search %}
{% product_search subcategory: ["4 Star", "5 Star"] %}
{% endproduct_search %}URL: ?search[subcategory]=Wellness
city
Filter by experience location city. Case-insensitive. Accepts a single value or an array. Products without an address are excluded.
{% product_search city: "London" %}
{% endproduct_search %}
{% product_search city: ["London", "Paris"] %}
{% endproduct_search %}URL: ?search[city]=London or ?search[city][]=London&search[city][]=Paris
country
Filter by country. Accepts ISO 3166 alpha-2 codes (e.g. "FR") or country names (e.g. "France"). Accepts a single value or an array.
{% product_search country: "FR" %}
{% endproduct_search %}
{% product_search country: ["FR", "DE"] %}
{% endproduct_search %}URL: ?search[country]=DE
departure_date
Filter by departure date using a range hash. Date values should be in YYYY-MM-DD format. A product matches if any of its slots fall within the specified range.
{% product_search departure_date: { greater_than: "2024-06-01", less_than: "2024-07-01" } %}
{% endproduct_search %}Liquid variables can be used for date values:
{% assign latest_date = selected_date %}
{% product_search departure_date: { less_than: latest_date } %}
{% endproduct_search %}URL: ?search[departure_date][greater_than]=2024-06-01&search[departure_date][less_than]=2024-07-01
departure_month
Filter by month across all years. Accepts a month number (1–12) or a 3-letter abbreviation (e.g. "Apr").
For example, departure_month: 4 returns products with departures in April of every year.
{% product_search departure_month: "Apr" %}
{% endproduct_search %}
{% product_search departure_month: 4 %}
{% endproduct_search %}URL: ?search[departure_month]=Apr or ?search[departure_month]=4
duration
Filter by experience duration using a range hash. Values represent the stored duration number.
Experience durations can represent hours or nights. duration: { equal_to: 2 } matches experiences with a duration of
2 regardless of the unit. Unlike experience_slot_search, product_search does not support duration_unit.
{% product_search duration: { greater_than: 3, less_than: 8 } %}
{% endproduct_search %}URL: ?search[duration][greater_than]=3&search[duration][less_than]=8
product_id
Restrict to specific experience ID(s). Accepts a single UUID or an array.
{% product_search product_id: "abcd1234-1234-abcd-1234-abcd1234abcd" %}
{% endproduct_search %}
{% assign product_ids = products | map: "id" %}
{% product_search product_id: product_ids %}
{% endproduct_search %}URL: ?search[product_id]=abcd1234-1234-abcd-1234-abcd1234abcd
series_id
Filter to products belonging to a specific series.
{% product_search series_id: "abcd1234-1234-abcd-1234-abcd1234abcd" %}
{% endproduct_search %}URL: ?search[series_id]=abcd1234-1234-abcd-1234-abcd1234abcd
exclude_sold_out_products
When true, excludes products where all variants are sold out.
{% product_search exclude_sold_out_products: true %}
{% endproduct_search %}URL: ?search[exclude_sold_out_products]=true
active_promotion
When true, returns only products with an active promotion. When false, returns only products without.
{% product_search active_promotion: true %}
{% endproduct_search %}include_organisation_products
When true, searches across all companies in the organisation rather than just the current company.
{% product_search include_organisation_products: true %}
{% endproduct_search %}sort
Sort order for results. Default: "departure_date_asc".
| Value | Description |
|---|---|
departure_date_asc | Departure date ascending (default). |
departure_date_desc | Departure date descending. |
name_asc | Product name A–Z. |
name_desc | Product name Z–A. |
duration_asc | Duration ascending. |
duration_desc | Duration descending. |
{% product_search sort: "departure_date_asc" %}
{% endproduct_search %}URL: ?search[sort]=departure_date_asc
page_size
Number of results per page. Default: 10. Tag attribute only — not available via URL.
{% product_search page_size: 12 %}
{% endproduct_search %}Tag categories
Products can be filtered by company-defined tag categories and values. This is only available as a URL query parameter — it cannot be passed as a tag attribute.
Filtering returns products that match at least one of the provided values in all filtered categories (AND across categories, OR within a category).
URL: ?search[Difficulty][]=Easy&search[Amenities][]=Pool
Examples:
?search[Difficulty][]=Easy— Products tagged Easy under Difficulty.?search[Difficulty][]=Easy&search[Difficulty][]=Moderate— Products tagged Easy or Moderate (OR within one category).?search[Amenities][]=Pool&search[Difficulty][]=Hard— Products tagged Pool under Amenities and Hard under Difficulty (AND across categories).
In Liquid, tag category values are available on the search object using bracket syntax:
{% if search %}
{{ search["Amenities"] }}
{% endif %}Range parameters
The departure_date and duration parameters accept a hash with comparison keys:
| Key | Description |
|---|---|
equal_to | Exact match. |
greater_than | Greater than value. |
greater_or_equal_than | Greater than or equal to value. |
less_than | Less than value. |
less_or_equal_than | Less than or equal to value. Tag attribute only — not available via URL. |
For product_search, greater_or_equal_than and less_or_equal_than each take priority over the other operators.
To filter a range, use greater_than with less_than together.
Pagination
Search results are paginated automatically.
Use the page_size parameter to control how many results are returned per page.
The paginate object can be used to build pagination controls:
{{ paginate | default_pagination }}Available months
The search exposes an available_months array that can be used to build month filters.
Each item is a Date object representing the first day of a month that contains at least one future experience departure.
available_months reflects all future departures company-wide. It is not filtered by the current search parameters.
A typical use case is building a month selector that submits a departure_month filter:
{% product_search %}
<select name="search[departure_month]">
{% for month in available_months %}
<option value="{{ month | date: '%m' }}">
{{ month | date: "%B %Y" }}
</option>
{% endfor %}
</select>
{% endproduct_search %}Accessing search parameters
The applied URL parameters are available through the search object when the page URL includes ?search[...] query parameters. If no search params are present, search is not defined.
{% if search %}
{{ search.departure_date }}
{{ search["Amenities"] }}
{% endif %}This is useful for preserving filter state in search forms and building navigable search interfaces.
Developer considerations
When implementing product search:
product_searchreturns Experience products only — not Accommodations or Packages.- If you want to return more than 12 results per page, use infinite scroll to append new results rather than increasing the pagination.
- Use
product_searchwhen each product should appear once. Useexperience_slot_searchwhen each departure date should be a separate result. - Use URL query parameters to build shareable search pages.
- Build month filters using
available_monthsrather than generating calendar months manually. - Use the
searchobject to preserve the current filter state in forms. Guard with{% if search %}as it is only defined when URL params are present. - Remember that URL query parameters override values passed directly to the tag.