How do I show Standard bundles on packing slips and order confirmations?

A Standard bundle reaches your orders as the individual products inside it, and Kitenzo links them together. Shopify can show them as one bundle with its contents underneath, but only if your order confirmation email and packing slip templates know about bundles. If either one lists every product separately, this guide fixes it.

1. Order confirmation email

Since December 2024, Shopify's default order confirmation shows a bundle as a single line with its contents listed underneath. If your email lists each product in the bundle with its own price, your template was customised before that change, so it didn't get the update.

Go to Settings > Notifications > Customer notifications > Order confirmation, then choose one of these:

  • Revert to default (quickest). Click Edit code, copy everything into a document as a backup, then click Revert to default. You'll lose any changes made to the template, so check the backup for anything you want to put back.
  • Keep your customisations and add Shopify's bundle code instead, following Shopify's guide: Bundles grouped view email notification template updates (the "Update order email notifications" section).

The same applies to the staff New order notification if your team works from it.

2. Packing slip

With this change, each bundle prints as one block: its name and how many items it holds, then its contents. Anything else in the order prints afterwards under Other items, along with any message a customer added to it, such as a gift card.

  1. In Shopify, go to Settings > Shipping and delivery, scroll to Packing slips and click Edit.
  2. Copy the whole template into a document as a backup before you change anything.
  3. Find the line {% for line_item in line_items_in_shipment %}. Select from that line down to its matching {% endfor %}. In Shopify's default template, that's the {% endfor %} just above {% unless includes_all_line_items_in_order %}.
  4. Replace the whole selection with the code below.
  5. Click Preview to check it shows without errors, then Save.
{%- comment -%} The bundle definitions Kitenzo stores on the order. Packing slips
  return attribute values HTML-escaped, so quotes are restored before parsing. {%- endcomment -%}
{%- assign bundles_json = '' -%}
{%- for attribute in order.attributes -%}
  {%- if attribute.first == '_bundles' -%}{%- assign bundles_json = attribute.last -%}{%- endif -%}
{%- endfor -%}
{%- assign bundles_json = bundles_json | replace: '"', '"' -%}

{%- assign boxes_done = '' -%}
{%- assign has_other_items = false -%}

<table style="width: 100%; border-collapse: collapse; margin-bottom: 1.5em;">
  {%- for line_item in line_items_in_shipment -%}
    {%- assign box_id = '' -%}
    {%- assign box_config_id = '' -%}
    {%- for property in line_item.properties -%}
      {%- if property.first == '_bundle_data' -%}
        {%- assign box_id = property.last | split: '#' | last -%}
        {%- assign box_config_id = property.last | split: '#' | first -%}
      {%- endif -%}
    {%- endfor -%}

    {%- if box_id == '' -%}
      {%- assign has_other_items = true -%}
      {%- continue -%}
    {%- endif -%}

    {%- assign box_key = '#' | append: box_id | append: '#' -%}
    {%- if boxes_done contains box_key -%}{%- continue -%}{%- endif -%}
    {%- assign boxes_done = boxes_done | append: box_key -%}

    {%- comment -%} The box's name: from Kitenzo's definitions first, then Shopify's own bundle grouping. {%- endcomment -%}
    {%- assign box_title = '' -%}
    {%- if bundles_json != '' -%}
      {%- assign chunks = bundles_json | split: '"configuredBundleId":' -%}
      {%- for chunk in chunks -%}
        {%- assign chunk_id = chunk | split: ',' | first | strip -%}
        {%- if chunk_id == box_config_id -%}
          {%- assign box_title = chunk | split: '"title":"' | last | split: '"' | first -%}
          {%- break -%}
        {%- endif -%}
      {%- endfor -%}
    {%- endif -%}
    {%- if box_title == '' and line_item.groups.size > 0 -%}{%- assign box_title = line_item.groups[0].title -%}{%- endif -%}
    {%- if box_title == '' -%}{%- assign box_title = 'Bundle' -%}{%- endif -%}

    {%- assign box_count = 0 -%}
    {%- for item in line_items_in_shipment -%}
      {%- for property in item.properties -%}
        {%- if property.first == '_bundle_data' -%}
          {%- assign item_box_id = property.last | split: '#' | last -%}
          {%- if item_box_id == box_id -%}{%- assign box_count = box_count | plus: item.shipping_quantity -%}{%- endif -%}
        {%- endif -%}
      {%- endfor -%}
    {%- endfor -%}

    <tr>
      <td colspan="2" style="padding: 10px 8px 6px; border-top: 2px solid #000; font-weight: bold; font-size: 1.1em;">
        {{ box_title }} ({{ box_count }} items)
      </td>
    </tr>
    {%- for item in line_items_in_shipment -%}
      {%- assign item_box_id = '' -%}
      {%- for property in item.properties -%}
        {%- if property.first == '_bundle_data' -%}{%- assign item_box_id = property.last | split: '#' | last -%}{%- endif -%}
      {%- endfor -%}
      {%- if item_box_id == box_id -%}
        <tr>
          <td style="padding: 3px 8px 3px 24px;">{{ item.title }}{% if item.variant_title != blank %} - {{ item.variant_title }}{% endif %}</td>
          <td style="padding: 3px 8px; text-align: right; white-space: nowrap;">&times; {{ item.shipping_quantity }}</td>
        </tr>
      {%- endif -%}
    {%- endfor -%}
  {%- endfor -%}

  {%- if has_other_items -%}
    <tr>
      <td colspan="2" style="padding: 10px 8px 6px; border-top: 2px solid #000; font-weight: bold; font-size: 1.1em;">Other items</td>
    </tr>
    {%- for item in line_items_in_shipment -%}
      {%- assign in_a_box = false -%}
      {%- for property in item.properties -%}
        {%- if property.first == '_bundle_data' -%}{%- assign in_a_box = true -%}{%- endif -%}
      {%- endfor -%}
      {%- unless in_a_box -%}
        <tr>
          <td style="padding: 3px 8px 3px 24px;">
            {{ item.title }}{% if item.variant_title != blank %} - {{ item.variant_title }}{% endif %}
            {%- comment -%} What the shopper wrote on a card line (To, From, Message), for the handwritten card. {%- endcomment -%}
            {%- for property in item.properties -%}
              {%- assign property_initial = property.first | slice: 0 -%}
              {%- if property.last != blank and property_initial != '_' -%}
                <br><span style="font-size: 0.9em;">{{ property.first }}: {{ property.last }}</span>
              {%- endif -%}
            {%- endfor -%}
          </td>
          <td style="padding: 3px 8px; text-align: right; white-space: nowrap;">&times; {{ item.shipping_quantity }}</td>
        </tr>
      {%- endunless -%}
    {%- endfor -%}
  {%- endif -%}
</table>

If you've already added our earlier packing slip code from this article, restore your backup (or Shopify's default template) first, then follow the steps above.

3. Check it with a test order

Place a test order with a bundle in it, then check the confirmation email, and print its packing slip from the order page (Print > Print packing slip). If your template has been heavily customised and you're not sure which part to replace, send us a message and we'll help.

Did this answer your question? Thanks for the feedback There was a problem submitting your feedback. Please try again later.

Still need help? Contact Us Contact Us