The purpose of this post is to contain information, usecases, examples and discussions of the newly released Nested cart lines.
Itās still very early on, and Iām just testing the new things myself.
Feel free to add usecases and findings as a comment - Iāll link relevant stuff up here. Letās deep dive it collectively!
Test adding nested lines from frontend:
Adding parent and child together
const formData = {
'items': [
{
'id': 48838251381083,
'quantity': 1
},
{
'id': 48838251053403,
'parent_id': 48838251381083,
'quantity': 1
}]
};
fetch(window.Shopify.routes.root + 'cart/add.js', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(formData)
})
.then(response => {
return response.json();
})
.catch((error) => {
console.error('Error:', error);
});
This will add both variants to the cart with the following new props on the child line (48838251053403
):
"parent_relationship": {
"parent_key": "48838251381083:754a9a2be65aec51642aabac38291aa3"
},
"instructions": {
"can_remove": true,
"can_update_quantity": true
}
Adding parent and child separately
async function addParentThenChild() {
// Add parent item
await fetch(window.Shopify.routes.root + 'cart/add.js', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
items: [
{ id: 48838251381083, quantity: 1 }
]
})
});
// Add child item after parent
await fetch(window.Shopify.routes.root + 'cart/add.js', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
items: [
{ id: 48838251053403, parent_id: 48838251381083, quantity: 1 }
]
})
});
}
addParentThenChild();
This will successfully add the parent line but throw a 422 error for the child {"status":422,"message":"Parent line does not match any existing or added lines","description":"Parent line does not match any existing or added lines"}
To add the child later, youād have to get the line key of the parent and use parent_line_key
instead of the parent_id
.
const formData = {
'items': [
{
'id': 48838251053403,
'parent_line_key': '48838251381083:754a9a2be65aec51642aabac38291aa3',
'quantity': 1
}]
};
Adding via liquid and FormData
{%- form 'product', product -%}
<input type="hidden" name="id" value="{{ product.selected_or_first_available_variant.id }}">
<input type="hidden" name="items[0][id]" value="55041416233285">
<input type="hidden" name="items[0][quantity]" value="1">
<input type="hidden" name="items[0][parent_id]" value="{{ product.selected_or_first_available_variant.id }}">
<button type="submit" class="button w-full">Add to cart</button>
{%- endform -%}
Adding Cart API using FormData
{% form 'product', product, id: 'product-test-form' %}
<input type="hidden" name="id" value="{{ product.selected_or_first_available_variant.id }}">
<input type="hidden" name="items[0][id]" value="55041416233285">
<input type="hidden" name="items[0][quantity]" value="1">
<input type="hidden" name="items[0][parent_id]" value="{{ product.selected_or_first_available_variant.id }}">
<button type="submit" class="button w-full" id="add-to-cart-btn">Add to cart</button>
{% endform %}
{% javascript %}
document.addEventListener('DOMContentLoaded', function() {
const form = document.querySelector('#product-test-form');
form.addEventListener('submit', async function(e) {
e.preventDefault();
try {
const formData = new FormData(form);
const response = await fetch(`${Shopify.routes.root}cart/add.js`, {
method: 'POST',
body: formData,
headers: {
'X-Requested-With': 'XMLHttpRequest'
}
});
if (!response.ok) {
throw new Error('Network response was not ok');
}
const result = await response.json();
console.log(result);
} catch (error) {
console.error('Error:', error);
}
});
});
{% endjavascript %}
Other findings
Cart removal
If you remove the child line, it just deletes that part of the āgroupā.
If you remove the parent line, it will also remove the child line. However, there seems to be no indication of this, in the API responses
Post order management
Functions like āBuy againā and returns handles nested lines as separate lines. So āBuy againā adds them separately (not nested anymore).
Examples of how to visually group it in the cart
Thanks to @Gabrielle_Rea we found something that can work
Horizon implementation
Seems like Horizon has it implemented using pure CSS depending on cart items order always having parent>child.
Link to liquid part (setting the class)
Link to CSS
Seems to also work when adding separately: https://x.com/Curzey1/status/1955947166066684160
Usecases
I think the usecases stated in the docs actually represent it very well, but will leave this open in case someone finds some cool creative ones.
From docs
This enables use cases like product add-ons (warranties, protection plans, service fees) that attach to a product in cart. For example, you might attach an extended warranty to a TV. Unlike regular cart lines, nested cart lines are always ordered after their parent line, and are removed from cart if parent is removed.
Generally I just think itās great to have usescases like this abstract from bundles since the previous āfixā was to use cart transforms to merge (bundle) items.
Post frontend
I guess I will change the structure of this post, when we add more usecases, examples or other stuff.