Notification emails
Show a bundle as one grouped row with its SKU and total in order confirmation, shipping confirmation, and invoice emails.
By default, an order confirmation email lists every component of a box as its own row with its own price. This block regroups them into one row: the bundle's name and SKU, the components indented underneath, and one combined price.
Where to edit: Shopify admin → Settings → Notifications → pick a notification → Edit code.
The same block works in Order confirmation, Shipping confirmation, Order invoice, and any other notification that loops over line_items. Replace the loop that prints the items — {% for line in line_items %}, or {% for line in subtotal_line_items %} (the same list without tip lines) — with everything below. If yours uses subtotal_line_items, use that name wherever this snippet says line_items.
The row markup for the non-bundle items below is a bare-bones stand-in. Paste your template's own row markup in its place.
Back it up first
Copy the original template into a text file before editing. Shopify's Revert to default button exists, but it throws away all your other customizations too.
The snippet
How it works
_bundle_idis the grouping key. Two boxes from the same builder in one order have different ids, so they render as two separate groups. That's intentional.- Totals are summed from the components, not read from the bundle parent — the parent doesn't exist on the order.
final_line_pricealready includes every discount that applied (bundle pricing and any of your own automatic discounts), so the total in the email always matches what was charged. In merged mode the Cart Transform applies the bundle price to the merged line; in individual mode it's an automatic discount named after the bundle. - The strikethrough price only renders when the components were actually discounted, so a no-discount bundle shows a single clean price.
- The SKU comes from the visible
Bundle SKUproperty and is printed once in the header rather than on every child row. - The name comes from
_oxify_builder(individual mode) or_bundle_name(older merged lines). A merged box ordered today carries neither, so its header reads Bundle — see Getting the bundle's name to name it yourself, or lead with the SKU.
Gotchas
- Don't sort children by
_slot_indexwith Liquid'ssort— it's a string, so slot 10 would sort before slot 2.line_itemsalready comes back in cart order. - Per-item discount labels (your own "BUY 4 → 20% OFF" and similar) are dropped by this layout by design, since the group shows one combined price. To keep them, iterate
line.discount_allocationsinside the child loop. - Test with a real order. Shopify's notification preview uses fake line items with no properties, so nothing groups in the preview. Place a test order, then use Orders → the order → Resend email.
Next
Order Printer — invoices, pick lists, and return forms.