All things Shopify and commerce
Hello,
I’m looking to create a cascading filter system on my Shop page (Dawn theme). Here’s what I would like to do:
I want to create a filter block with 3 linked dropdown menus:
Brand (product metafield: “Brand:”)
Year (product metafield: “Year:”)
Model (my products)
The dropdowns should be cascading:
The selected Brand determines which Years are available.
The selected Year determines which Models are shown.
The values must be automatically pulled from existing product metafields (no manually entered or pre-generated values).
A “Search” button should display the products that match all 3 criteria.
Thank you in advance for your help
Hello @AdditivPlus To create a cascading filter system with dynamic metafield values and a search button on the Shop page in the Dawn theme, you’ll need to:
Key Features to Implement
1. Three dropdowns:
. Brand (from metafield: product.metafields.custom.brand)
. Year (from metafield: product.metafields.custom.year)
. Model (product title or handle)
2. Cascading logic:
. Brand → filters available Years.
. Year → filters available Models.
. “Search” button → filters and shows matching products.
3. Values must be dynamically generated from the product catalog — no hardcoded options.
Overview of the Implementation Plan
1. Create a Section (or modify a template) to render the 3 dropdowns.
2. Collect metafields dynamically via JavaScript from products listed on the page (or fetched via Shopify's Storefront API).
3. Use JavaScript to:
. Filter dropdown options based on the previous selection.
. Filter products once "Search" is clicked.
Suggested Implementation Approach
1. Add Metafields to Your Products
Ensure the following metafields exist on each product in Settings > Custom data > Products:
. Namespace: custom
. Key: brand (single-line text)
. Key: year (single-line text)
2. Create the Filter Block
In sections/main-collection-product-grid.liquid (or a custom section on the Shop page), add this HTML:
<div id="cascading-filters">
<select id="brand-filter">
<option value="">Select Brand</option>
</select>
<select id="year-filter" disabled>
<option value="">Select Year</option>
</select>
<select id="model-filter" disabled>
<option value="">Select Model</option>
</select>
<button id="filter-search" disabled>Search</button>
</div>
3. Expose Product Metafields in Liquid
Below the product grid, expose the data in a JavaScript-readable way:
<script>
window.productData = [
{% for product in collection.products %}
{
id: {{ product.id }},
handle: "{{ product.handle }}",
title: "{{ product.title | escape }}",
brand: "{{ product.metafields.custom.brand | escape }}",
year: "{{ product.metafields.custom.year | escape }}"
}{% unless forloop.last %},{% endunless %}
{% endfor %}
];
</script>
4. Add JavaScript for Cascading Logic
<script>
document.addEventListener("DOMContentLoaded", function () {
const data = window.productData;
const brandFilter = document.getElementById("brand-filter");
const yearFilter = document.getElementById("year-filter");
const modelFilter = document.getElementById("model-filter");
const searchBtn = document.getElementById("filter-search");
// Get unique values
const brands = [...new Set(data.map(p => p.brand).filter(Boolean))];
brands.forEach(brand => {
const option = new Option(brand, brand);
brandFilter.appendChild(option);
});
brandFilter.addEventListener("change", () => {
const selectedBrand = brandFilter.value;
yearFilter.innerHTML = `<option value="">Select Year</option>`;
modelFilter.innerHTML = `<option value="">Select Model</option>`;
yearFilter.disabled = !selectedBrand;
modelFilter.disabled = true;
searchBtn.disabled = true;
if (selectedBrand) {
const years = [...new Set(data.filter(p => p.brand === selectedBrand).map(p => p.year).filter(Boolean))];
years.forEach(year => {
yearFilter.appendChild(new Option(year, year));
});
}
});
yearFilter.addEventListener("change", () => {
const selectedBrand = brandFilter.value;
const selectedYear = yearFilter.value;
modelFilter.innerHTML = `<option value="">Select Model</option>`;
modelFilter.disabled = !selectedYear;
searchBtn.disabled = true;
if (selectedBrand && selectedYear) {
const models = data
.filter(p => p.brand === selectedBrand && p.year === selectedYear)
.map(p => ({ title: p.title, handle: p.handle }));
models.forEach(m => {
modelFilter.appendChild(new Option(m.title, m.handle));
});
}
});
modelFilter.addEventListener("change", () => {
searchBtn.disabled = !modelFilter.value;
});
searchBtn.addEventListener("click", () => {
const selectedHandle = modelFilter.value;
if (selectedHandle) {
window.location.href = `/products/${selectedHandle}`;
}
});
});
</script>
Optional Enhancements
. Replace <select> with Tailwind dropdowns or custom UI components.
. Use AJAX instead of page reloads to dynamically render filtered products.
. Replace navigation with a filtered collection grid if multiple models match (instead of redirecting to 1 product).
try this :
Thank you 😊
Hi, thanks for your help !
The block with the three dropdown menus is displaying correctly so that part is working.
However, the menus don’t seem to be pulling data from the metafields, so no values are showing up at the moment and nothing can be selected.
Thanks in advance for your help !
hey @AdditivPlus Thanks for the update! If the dropdowns are visible but not populating with values, that usually means one of the following is happening:
Let's Troubleshoot Step-by-Step
1. Are the metafields available in Liquid?
Ensure you're exposing them correctly inside your Shopify Liquid file. Here's a safe debug version of the window.productData script:
<script>
window.productData = [
{% for product in collection.products %}
{
id: {{ product.id }},
handle: "{{ product.handle }}",
title: "{{ product.title | escape }}",
brand: "{{ product.metafields.custom.brand.value | escape }}",
year: "{{ product.metafields.custom.year.value | escape }}"
}{% unless forloop.last %},{% endunless %}
{% endfor %}
];
console.log("Loaded product data:", window.productData);
</script>
Check the browser console (right-click → Inspect → Console tab) and confirm if:
. The array appears with objects.
. brand and year fields are not null, undefined, or empty.
2. Confirm Metafield Namespace and Keys
Double-check:
. Product metafields are under namespace: custom
. Keys are:
. brand
. year
. Products in the current collection have non-empty values for both
Go to Shopify Admin > Settings > Custom data > Products to confirm.
3. Are you on a collection page with products?
If you're testing this on a page that’s not a collection, then collection.products will be empty.
You can fix this by loading all products via the Storefront API, but for now, verify you're testing this on a real collection page.
Optional Debug Tip
To make sure you're pulling values, insert this line temporarily in your Liquid:
{% for product in collection.products %}
<p>{{ product.title }} - Brand: {{ product.metafields.custom.brand }} - Year: {{ product.metafields.custom.year }}</p>
{% endfor %}
If this shows blank for brand or year, then the metafields are either:
. Not assigned to those products
. Not correctly referenced in Liquid
try this if you need any plz let me know
thank you 😊
Thanks for your reply!
I’ve double-checked and re-added the window.productData script as you suggested.
However, it still doesn’t display the products in the console. When inspecting the page, this is what I see:
Yet, I’ve properly entered the metafields in Shopify’s settings, and they are correctly assigned to each product.
For example, for the voltage converter:
Thanks in advance for your help!
You’ll need custom JavaScript to make the dropdowns update based on each other. Use Shopify Liquid to pull the metafield values, then trigger changes on each dropdown to update the next one. A search button can reload the page with filters in the URL.
Learn how to build powerful custom workflows in Shopify Flow with expert guidance from ...
By Jacqui May 7, 2025Did You Know? May is named after Maia, the Roman goddess of growth and flourishing! ...
By JasonH May 2, 2025Discover opportunities to improve SEO with new guidance available from Shopify’s growth...
By Jacqui May 1, 2025