Skip to main content

GUIDE: Show bundle components grouped on your packing slip

Update your Shopify packing slip template to group bundle products.

Updated today

Customisation Guide

In this guide we’ll update your Shopify packing slip template so you can safely replace the existing code with a new bundle-friendly template.

In this guide:

  • We’ll open and edit your packing slip template

  • We’ll save a backup of your current template

  • We’ll paste in your new template code

  • We’ll test the updated packing slip, and restore the old version if needed

This is an advanced guide intended for merchants comfortable with editing HTML/Liquid, or for your developer.


Before we begin

A few important notes:

  • Packing slip changes are live immediately after you click Save.

  • There is no preview/draft mode, so always keep a backup of your original template.

  • This guide only affects your packing slip template. It doesn’t change invoices, emails, or your online store theme.

If editing template code isn’t your cup of tea, feel free to reach out to the Biscuits Bundles team and we can help you out.

Third party apps
If you’re using a third-party app to generate or customise your packing slips, you’ll need to update the packing slip layout inside that app rather than in Shopify’s default template editor. This guide is designed for updating the default Shopify packing slip template only. If your store uses an app-based packing slip, please refer to that app’s help documentation or contact their support team and ask how to update your packing slip layout to clearly show bundle products and their component items.


1. Open your packing slip template

First, we’ll open the packing slip template editor in Shopify.

  1. In Shopify admin, go to Settings and open Shipping and delivery

  2. Scroll down to Shipping documents packing slip template

You should now see the packing slip template code (HTML and Liquid).


2. Save a backup of your current template

Before making any edits, save a copy of your existing template so you can revert later if needed.

  1. In the template editor, select all of the code.

  2. Copy the code and paste it into a safe place (for example, a text file or internal doc).

  3. Give it a clear name, such as:

    Packing slip template backup – [Store Name] – [Date]

Once saved, this backup can be pasted back into the template editor at any time.


3. Replace the template with your new code

Next, we’ll replace the current template with your new packing slip code.

  1. In the packing slip editor, select all existing code.

  2. Paste in your new packing slip template code so it fully replaces the old version.

  3. Click Preview template - this will show you a preview of the new code but you can't choose which order it will use in the preview

  4. Click Save.

Packing slip Code

This packing slip code is based on the default packing slip template provided by Shopify, with adjustments to group all Shopify native bundles.

<div class="wrapper">
<div class="header">
<div class="shop-title">
<p class="to-uppercase">
{{ shop.name }}
</p>
</div>
<div class="order-title">
<p class="text-align-right">
Order {{ order.name }}
</p>
{% if order.po_number != blank %}
<p class="text-align-right">
PO number #{{ order.po_number }}
</p>
{% endif %}
<p class="text-align-right">
{{ order.created_at | date: format: "date" }}
</p>
</div>
</div>
<div class="customer-addresses">
<div class="shipping-address">
<p class="subtitle-bold to-uppercase">
{% if delivery_method.instructions != blank %}
Delivery to
{% else %}
Ship to
{% endif %}
</p>
<p class="address-detail">
{% if shipping_address != blank %}
{{ shipping_address.name }}
{% if shipping_address.company != blank %}
<br>
{{ shipping_address.company }}
{% endif %}
<br>
{{ shipping_address.address1 }}
{% if shipping_address.address2 != blank %}
<br>
{{ shipping_address.address2 }}
{% endif %}
{% if shipping_address.city_province_zip != blank %}
<br>
{{ shipping_address.city_province_zip }}
{% endif %}
<br>
{{ shipping_address.country }}
{% if shipping_address.phone != blank %}
<br>
{{ shipping_address.phone }}
{% endif %}
{% else %}
No shipping address
{% endif %}
</p>
</div>
<div class="billing-address">
<p class="subtitle-bold to-uppercase">
Bill to
</p>
<p class="address-detail">
{% if billing_address != blank %}
{{ billing_address.name }}
{% if billing_address.company != blank %}
<br>
{{ billing_address.company }}
{% endif %}
<br>
{{ billing_address.address1 }}
{% if billing_address.address2 != blank %}
<br>
{{ billing_address.address2 }}
{% endif %}
{% if billing_address.city_province_zip != blank %}
<br>
{{ billing_address.city_province_zip }}
{% endif %}
<br>
{{ billing_address.country }}
{% else %}
No billing address
{% endif %}
</p>
</div>
</div>
<hr>
<div class="order-container">
<div class="order-container-header">
<div class="order-container-header-left-content">
<p class="subtitle-bold to-uppercase">
Items
</p>
</div>
<div class="order-container-header-right-content">
<p class="subtitle-bold to-uppercase">
Quantity
</p>
</div>
</div>

{% assign desired_image_size = 58 %}
{% assign resolution_adjusted_size = desired_image_size | times: 300 | divided_by: 72 | ceil %}
{% capture effective_image_dimensions %}
{{ resolution_adjusted_size }}x{{ resolution_adjusted_size }}
{% endcapture %}

{% liquid
assign sorted_line_items = line_items_in_shipment | sort: 'title'
assign item_groups = sorted_line_items | map: 'groups' | uniq
%}

{% if item_groups != blank %}
{% liquid
assign groups = ''
for line_item in order.line_items
unless line_item.groups[0].title == blank
assign groups = groups | append: '#*#' | append: line_item.groups[0].display_title | append: '##' | append: line_item.id
endunless
endfor
assign group_list = groups | remove_first: '#*#' | split: '#*#' | sort

assign last_group_name = ''
%}

{% for group in group_list %}
{% liquid
assign group_name = group | split: '##' | first
assign line_item_id = group | split: '##' | last | plus: 0
assign line_item = order.line_items | where: 'id', line_item_id | first
assign shipping_line_item = line_items_in_shipment | where: 'id', line_item_id | first

assign bundle_image_url = ''
assign bundle_image_url = line_item.product.featured_image | product_img_url: 'thumb'

assign shipping_qty = shipping_line_item.shipping_quantity
if shipping_qty == blank
assign shipping_qty = line_item.quantity
endif
%}

{% if last_group_name != group_name %}
{% capture bundle_parent_image_tag %}
{% if line_item.groups and line_item.groups.size > 0 and line_item.groups[0].image != blank %}
{{ line_item.groups[0].image | img_url: effective_image_dimensions | img_tag: '', 'aspect-ratio__content' }}
{% endif %}
{% endcapture %}

<div class="flex-line-item flex-line-item-bundle">
<div class="flex-line-item-img">
{% if bundle_parent_image_tag != blank %}
<div class="aspect-ratio aspect-ratio-square" style="width: {{ desired_image_size }}px; height: {{ desired_image_size }}px;">
{{ bundle_parent_image_tag }}
</div>
{% endif %}
</div>
<div class="flex-line-item-description">
<span class="line-item-bundle-parent-name">{{ group_name }}</span>
{% if line_item.properties and line_item.properties._biscuits_sku != blank %}
<span class="line-item-bundle-parent-sku">{{ line_item.properties._biscuits_sku }}</span>
{% endif %}
</div>
<div class="flex-line-item-quantity"></div>
</div>
{% endif %}

<div class="flex-line-item flex-line-item--bundle">
<div class="flex-line-item-img">
{% if line_item.image != blank %}
<div class="aspect-ratio aspect-ratio-square" style="width: {{ desired_image_size }}px; height: {{ desired_image_size }}px;">
{{ line_item.image | img_url: effective_image_dimensions | img_tag: '', 'aspect-ratio__content' }}
</div>
{% endif %}
</div>
<div class="flex-line-item-description">
<p>
<span class="line-item-description-line">
{{ line_item.title }}
</span>
{% if line_item.variant_title != blank %}
<span class="line-item-description-line">
{{ line_item.variant_title }}
</span>
{% endif %}
{% if line_item.sku != blank %}
<span class="line-item-description-line">
{{ line_item.sku }}
</span>
{% endif %}
</p>
</div>
<div class="flex-line-item-quantity">
<p class="text-align-right">
{{ shipping_qty }} of {{ line_item.quantity }}
</p>
</div>
</div>

{% assign last_group_name = group_name %}
{% endfor %}
{% endif %}

{% comment %}
Non-bundle / standalone items
{% endcomment %}
{% for line_item in line_items_in_shipment %}
{% if line_item.groups.size > 0 %}
{% continue %}
{% endif %}

<div class="flex-line-item">
<div class="flex-line-item-img">
{% if line_item.image != blank %}
<div class="aspect-ratio aspect-ratio-square" style="width: {{ desired_image_size }}px; height: {{ desired_image_size }}px;">
{{ line_item.image | img_url: effective_image_dimensions | img_tag: '', 'aspect-ratio__content' }}
</div>
{% endif %}
</div>
<div class="flex-line-item-description">
<p>
<span class="line-item-description-line">
{{ line_item.title }}
</span>
{% if line_item.variant_title != blank %}
<span class="line-item-description-line">
{{ line_item.variant_title }}
</span>
{% endif %}
{% if line_item.sku != blank %}
<span class="line-item-description-line">
{{ line_item.sku }}
</span>
{% endif %}
{% for group in line_item.groups %}
<span class="line-item-description-line">
Part of: {{ group.title }}
</span>
{% endfor %}
</p>
</div>
<div class="flex-line-item-quantity">
<p class="text-align-right">
{{ line_item.shipping_quantity }} of {{ line_item.quantity }}
</p>
</div>
</div>
{% endfor %}
</div>

{% unless includes_all_line_items_in_order %}
<hr class="subdued-separator">
<p class="missing-line-items-text ">
There are other items from your order not included in this shipment.
</p>
{% endunless %}
<hr>
{% if order.note != blank %}
<div class="notes">
<p class="subtitle-bold to-uppercase">
Notes
</p>
<p class="notes-details">
{{ order.note }}
</p>
</div>
{% endif %}
{% if delivery_method.instructions != blank %}
<div class="notes">
<p class="subtitle-bold to-uppercase">
Delivery instructions
</p>
<p class="notes-details">
{{ delivery_method.instructions }}
</p>
</div>
{% endif %}
<div class="footer">
<p>
Thank you for shopping with us!
</p>
<p>
<strong>
{{ shop.name }}
</strong>
<br>
{{ shop_address.summary }}
<br>
{{ shop.email }}
<br>
{{ shop.domain }}
</p>
</div>
</div>
<style type="text/css">
body {
font-size: 15px;
}

* {
box-sizing: border-box;
}

.wrapper {
width: 831px;
margin: auto;
padding: 4em;
font-family: "Noto Sans", sans-serif;
font-weight: 250;
}

.header {
width: 100%;
display: -webkit-box;
display: -webkit-flex;
display: flex;
flex-direction: row;
align-items: top;
}

.header p {
margin: 0;
}

.shop-title {
-webkit-box-flex: 6;
-webkit-flex: 6;
flex: 6;
font-size: 1.9em;
}

.order-title {
-webkit-box-flex: 4;
-webkit-flex: 4;
flex: 4;
}

.customer-addresses {
width: 100%;
display: inline-block;
margin: 2em 0;
}

.address-detail {
margin: 0.7em 0 0;
line-height: 1.5;
}

.subtitle-bold {
font-weight: bold;
margin: 0;
font-size: 0.85em;
}

.to-uppercase {
text-transform: uppercase;
}

.text-align-right {
text-align: right;
}

.shipping-address {
float: left;
min-width: 18em;
max-width: 50%;
}

.billing-address {
padding-left: 20em;
min-width: 18em;
}

.order-container {
padding: 0 0.7em;
padding-bottom: 1rem;
display: flex;
flex-direction: column;
align-items: flex-start;
justify-content: flex-start;
}

.order-container-header {
display: inline-block;
width: 100%;
margin-top: 1.4em;
}

.order-container-header-left-content {
float: left;
}

.order-container-header-right-content {
float: right;
}

.flex-line-item {
display: -webkit-box;
display: -webkit-flex;
display: flex;
flex-direction: row;
align-items: center;
page-break-inside: avoid;
width: 100%;
height: auto;
line-height: 1;
margin: 0.5em 0;
}

.flex-line-item-img {
margin-right: 1.4em;
min-width: {{ desired_image_size }}px;
}

.flex-line-item-description {
-webkit-box-flex: 7;
-webkit-flex: 7;
flex: 7;
}

.line-item-description-line {
display: block;
}

.flex-line-item-description p {
margin: 0;
line-height: 1.5;
}

.flex-line-item-quantity {
-webkit-box-flex: 3;
-webkit-flex: 3;
flex: 3;
line-height: 1;
text-align: right;
}

.flex-line-item-quantity p {
line-height: 1;
}

.subdued-separator {
height: 0.07em;
border: none;
color: lightgray;
background-color: lightgray;
margin: 0;
}

.missing-line-items-text {
margin: 1.4em 0;
padding: 0 0.7em;
}

.notes {
margin-top: 2em;
}

.notes p {
margin-bottom: 0;
}

.notes .notes-details {
margin-top: 0.7em;
}

.footer {
margin-top: 2em;
text-align: center;
line-height: 1.5;
}

.footer p {
margin: 0;
margin-bottom: 1.4em;
}

hr {
height: 0.14em;
border: none;
color: black;
background-color: black;
margin: 0;
}

.aspect-ratio {
position: relative;
display: block;
background: #fafbfc;
padding: 0;
}

.aspect-ratio::before {
z-index: 1;
content: "";
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
border: 1px solid rgba(195,207,216,0.3);
}

.aspect-ratio--square {
width: 100%;
padding-bottom: 100%;
}

.aspect-ratio__content {
position: absolute;
max-width: 100%;
max-height: 100%;
display: block;
top: 0;
right: 0;
bottom: 0;
left: 0;
margin: auto;
}

.line-item-bundle-parent-sku {
font-size: 14px;
margin-top: 0.5em;
display: block;
}

.flex-line-item-bundle {
margin-top: 1.4em;
}

.flex-line-item--bundle {
padding-left: 1rem;
padding-bottom: 0;
margin: 0.5em 0;
}

.flex-line-item--bundle .flex-line-item-description {
padding-left: 2rem;
}

.flex-line-item--bundle + .flex-line-item:not(.flex-line-item--bundle):not(.flex-line-item-bundle) {
margin-top: 1em;
}
</style>

Your store is now using the new packing slip template for all future packing slips.


4. Test your updated packing slip

Now we’ll confirm everything looks as expected.

  1. In Shopify admin, go to Orders and open a recent order (preferably one with a bundle purchased).

  2. Click More actions → Print packing slip.

  3. Check that:

    • Product titles and quantities look correct

    • Bundle products and component items are grouped or indented as expected

    • The layout looks reasonable when printed or saved as a PDF

If the output doesn’t look right, you can restore your backup from the next step.


5. Restore your backup template (if needed)

If you want to undo the changes and go back to your original packing slip:

  1. In Shopify admin, go to Settings and open Shipping and delivery

  2. Scroll down to Shipping documents packing slip template

  3. Select all of the current code in the editor.

  4. Paste in the backup template you saved earlier.

  5. Click Save.

Your packing slip will now use the original template again.


Conclusion

That’s it! You’ve now updated your packing slip template and know how to restore the previous version if needed.

Stuck or got questions?

Use the chat widget below, or reach out to us at [email protected]

Happy coding, and thanks for teaming up with Biscuits Bundles!

Did this answer your question?