Order confirmation email shows nested cart line items twice

When using parent_id in /cart/add.js to create nested cart lines (e.g., a main product with a free gift nested underneath), the default order confirmation email renders the parent line item twice:

  1. Once as a standalone item in the subtotal_line_items loop (because line.groups.size == 0 and it’s not a nested_line_child?)
  2. Once as a deliverable group in the line_item_groups loop (with its children correctly shown underneath)

The subtotal/total calculations are correct — only the visual display is duplicated.

Steps to reproduce:

  1. Add items to cart via /cart/add.js with a parent_id field on one of the items, creating a parent-child nested line relationship:
  {
    "items": [
      { "id": 1234, "quantity": 2 },
      { "id": 5678, "quantity": 1, "parent_id": 1234 }
    ]
  }
  1. Complete checkout
  2. View the order confirmation email

Expected: The parent item appears once, with its child item nested underneath.

Actual: The parent item appears twice — once standalone (without children) and once with children nested underneath.

Checkout

Email

Could anyone have a look please? We are getting tickets from confused merchants daily but would like to keep using the new nested cart lines.

Hey @dostu, thanks for reporting this.

I was able to replicate this on my test store as well. I’m looking in to this and will report back here with what I find.

Hey @dostu, following up here, our team is looking in to this. I don’t have a timeframe on a resolution, but I will update here with any changes.

Any updates on this? We are constantly getting tickets from our merchants about this.

The issue seems to be that nested_line_parent? and nested_line_child? never fire for parent_id-nested items.

I have checked and the issue is still open, although not resolved. We are tracking this and will update when there is a resolution.

In the meantime, have you implemented any workarounds? For example updating the template?

@KyleG-Shopify We’re facing the same issue. As a temporary workaround, we’ll turn off this feature. In the meantime, we hope you can resolve it.

@KyleG-Shopify any progress on this topic? I tried a couple of days ago and it was still an issue.

@KyleG-Shopify Is there any progress on this matter? my client is facing the same issue today.

Hey all, thanks for checking in. This is currently not yet resolved.

@KyleG-Shopify any updates on this ? We are facing the same issue

Surely the ticket priority should be higher on this? It’s a core feature.

Hey everyone, Until Shopify come up with any solution use this workaround which I have tested with Order confirmation email template with default order confirmation template:

  1. Create a backup of current order confirmation email template.

  2. Add this liquid code to the top of the order confirmation email:
    (This will save all parent variant ids inside variable for later use)

    {% capture component_parent_variant_ids %}
    {% for group in line_item_groups %}
    {% if group.components.size > 0 %}
    {% assign parent_item = group.parent_sales_line_item %}
    {{ parent_item.variant_id }},
    {% endif %}
    {% endfor %}
    {% endcapture %}
    
    {% assign component_parent_variant_ids = component_parent_variant_ids | remove: ' ' | strip_newlines %}
    
  3. Later on find this line in email template (which is line items loop appears in 2 places) {% for line in subtotal_line_items %} using CTRL+F or Command+F and right after this line enter into new line and copy paste this code

    {% if component_parent_variant_ids contains line.variant.id %}
    {% continue %}
    {% endif %}

    This will basically skip the line items which are parent variants.

  4. If order confirmation email template is modified then make sure the if condition variable which is line (from line.variant.id) in this case should be same as the variable name in the loop at for line in subtotal_line_items

Thanks, this works - I tested the same idea on a customized order confirmation template.

One edge case to watch: skipping by variant_id will also hide a second copy of the same product if it’s in the order without a nested child. Matching on line.id instead keeps that line.

contains is also a substring check, so wrapping IDs as ,{id}, avoids accidental matches. I also filtered with group.deliverable? so bundle parents aren’t mixed in with nested add-ons.

{% continue %} in the subtotal_line_items loops is the right call though - thanks for posting this.

Yes that’s right, In the case where the same product is added without nested product it will skip that but it will also show it as quantity like below:

and this is the case where same product is added as bundle and nested parent and as separate product and will show like this