Shopify themes, liquid, logos, and UX
Hello,
I am working on a motorcycle parts store.
I created a custom page page.motorcycle.liquid that takes the motorId from the URL and sends a request to my own backend in order to find the products that are compatible with that motorId. Then I use the fetch API to get the shopify products based on the handles of the products and I want to render them using the already existent card-product.liquid snippet.
<script>
document.addEventListener('DOMContentLoaded', function() {
const urlParams = new URLSearchParams(window.location.search);
const motorId = urlParams.get('motorId');
// Fetch products from backend
fetch(`https://myapi/parts/${motorId}`)
.then(response => response.json())
.then(productHandles => {
displayProducts(productHandles);
})
});
</script>
I tried to display the products using the Section Rendering API, but i failed:
function displayProducts(productHandles) {
const productPromises = productHandles.map(productHandle => {
return fetch(`/products/${productHandle}.json`)
.then(response => response.json())
.then(data => data.product)
});
Promise.all(productPromises)
.then(products => {
products.map(product=>{
const sectionUrl = `/sections/custom-product-section?product_id=${product.id}`;
const response = fetch(sectionUrl);
if (!response.ok) {
throw new Error('Failed to fetch section');
}
const html = response.text();
document.getElementById('motor-parts').innerHTML += html;
})
})
.catch(error => {
console.error(error);
});
}
The custom section I created is custom-product-section.liquid:
{% assign product_id = section.settings.product_id %}
<div id="product-section">
{% assign product = all_products[product_id] %}
{% if product %}
{% render 'card-product', card_product: product %}
{% endif %}
</div>
{% schema %}
{
"name": "Custom Product",
"settings": [
{
"type": "text",
"id": "product_id",
"label": "Product ID",
"default": ""
}
]
}
{% endschema %}
The error I get is 404 when I try to fetch the section html.
I would appreciate some help regarding what I am doing wrong about that section. Or if I should approach rendering the products in another way?
Solved! Go to the solution
This is an accepted solution.
You're using section rendering API in a wrong way.
First, you can't pass any data/parameters into section.
Second, the url should be an url of a page on your site and section id comes as parameter. Like https://store.name/?sections=main_product_page.
If you're getting a list of handles from your API there is no need to fetch product data since liquid does not really use product id.
What you can do is to create an alternate product page template with {% layout none %} which will actually render the product card HTML:
templates/product.card.liquid
{% layout none %}
<li class=product-card-wrapper>
{% render 'card-product', card_product: product %}
</li>
and then have something like this in your JS
function displayProducts(productHandles) {
const productPromises = productHandles.map(productHandle => {
return fetch(`/products/${productHandle}?view=card`)
.then(response => response.text())
});
Promise.all(productPromises)
.then( productItems => productsItems.join(' ') )
.then( productGridInnerHTML => {
document.getElementById('motor-parts').innerHTML = `<ul class=product-grid> ${ productGridInnerHTML } </ul>`
});
}
This is an accepted solution.
You're using section rendering API in a wrong way.
First, you can't pass any data/parameters into section.
Second, the url should be an url of a page on your site and section id comes as parameter. Like https://store.name/?sections=main_product_page.
If you're getting a list of handles from your API there is no need to fetch product data since liquid does not really use product id.
What you can do is to create an alternate product page template with {% layout none %} which will actually render the product card HTML:
templates/product.card.liquid
{% layout none %}
<li class=product-card-wrapper>
{% render 'card-product', card_product: product %}
</li>
and then have something like this in your JS
function displayProducts(productHandles) {
const productPromises = productHandles.map(productHandle => {
return fetch(`/products/${productHandle}?view=card`)
.then(response => response.text())
});
Promise.all(productPromises)
.then( productItems => productsItems.join(' ') )
.then( productGridInnerHTML => {
document.getElementById('motor-parts').innerHTML = `<ul class=product-grid> ${ productGridInnerHTML } </ul>`
});
}
Thank you so much, Tim.
Seems to be working. Can you also help me how to figure how to get the ?view=card parameter to work?
Now it just returns the whole product page with header, footer and other sections. The ?view=card does not seem to make a difference. I tried to edit the product.json file from templates, but no luck. I want the /product/handle URL to work normally, but when the parameter ?view=card is added it should render a minimal product template (card-product.liquid).
You're getting the whole product page because your alternate template is not recognised.
In this case Shopify falls back to the default one.
I can't really say what's wrong in your setup, but to make ?view=card work you need to have an alternate product page template with suffix card, as I've suggested above. Here it's just much simpler to have it as a .liquid template rather then create a json template, then create a section, etc...
And the layout tag should take care of headers, footers and other stuff we do not need here.
Thank you again for the response.
I had a typo in the template file name: I wrote product-card.liquid instead of product.card.liquid. I did not realize it actually parses the file name in order to get the card suffix.
Cool! Message with the URL when it's live -- would be interesting to have a look.
Learn how to expand your operations internationally with Shopify Academy’s learning path...
By Shopify Feb 4, 2025Hey Community, happy February! Looking back to January, we kicked off the year with 8....
By JasonH Feb 3, 2025Expand into selling wholesale with Shopify Academy’s learning path, B2B on Shopify: Lau...
By Shopify Jan 28, 2025