Contact us
← BlogGuide · Orders

How to show personalisation on your Shopify packing slips (free template)

Shopify's packing slip leaves out what the customer typed: names, engraving text, uploads. Here's a free template that prints every personalisation field, in 5 minutes.

· 4 min read

How to show personalisation on your Shopify packing slips (free template)

A customer orders a necklace and types “Emma” in the engraving box. You can see “Emma” on the order page in Shopify. Then you print the packing slip, and it only says “Engraved Necklace”. The name isn’t there.

So you click into each order before you pack it, one by one. With five orders a day that’s annoying. With forty, it’s an hour gone, and every time you copy a name by hand you can misread it.

The reason is simple: Shopify’s default packing slip prints the product title, the variant and the SKU, but not the custom text the customer typed. Shopify calls that text line item properties, and the default slip doesn’t include them.

You can fix it yourself in about 5 minutes, for free, with no app and no developer. This works the same whether you call it personalisation or personalization, a packing slip or a packaging slip.

The free fix, step by step

1. Open the packing slip editor

In Shopify admin, go to Settings → Shipping and delivery. Scroll down to Documents and click Packing slip. You’ll see the code that builds your slip.

2. Replace it with our template

Select all the code in the editor, delete it, and paste our template in its place. Then click Save.

You can download the template or copy it from the box below. It’s Shopify’s own default packing slip with one small block added, so everything else on your slip stays the same.

Show the full template (copy all of it)
<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{% if delivery_method.type == "pick-up" %} pickup-addresses{% endif %}">
    {% unless delivery_method.type == "pick-up" %}
      <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>
    {% endunless %}
    <div class="billing-address{% if delivery_method.type == "pick-up" %} pickup-billing-address{% endif %}">
      <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>

    {% comment %}
    To adjust the size of line item images, change desired_image_size.
    The other variables make sure your images print at high quality.
    {% endcomment %}
    {% 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 %}

    {% for line_item in line_items_in_shipment %}
      <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 %}
              {% if group.deliverable? %}
                <span class="line-item-description-line">
                  For: {{ group.title }}
                </span>
              {% else %}
                <span class="line-item-description-line">
                  Part of: {{ group.title }}
                </span>
              {% endif %}
            {% endfor %}
            {% comment %} Personalisation (names, engraving text, uploads). Hidden "_" fields are skipped. {% endcomment %}
            {% for property in line_item.properties %}
              {% assign first_char = property.first | slice: 0 %}
              {% if first_char != '_' and property.last != blank %}
                <span class="line-item-description-line">
                  {{ property.first }}: {% if property.last contains '/uploads/' %}File uploaded{% else %}<strong style="font-weight:700">{{ property.last | newline_to_br }}</strong>{% endif %}
                </span>
              {% endif %}
            {% 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 %}
    {% unless delivery_method.type == "pick-up" %}
      <hr class="subdued-separator">
      <p class="missing-line-items-text ">
        There are other items from your order not included in this shipment.
      </p>
    {% endunless %}
  {% 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;
  }

  .pickup-billing-address {
    padding-left: 0;
  }

  .order-container {
    padding: 0 0.7em;
  }

  .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;
    margin: 1.4em 0;
    page-break-inside: avoid;
  }

  .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;
  }

  .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;
  }
</style>

3. Test it on a real order

Open an order that has personalisation, then click More actions → Print packing slip. Every field the customer filled in now prints under its product, in bold:

  • Name: Emma
  • Font: Script
  • Gift message: Happy birthday, love Mum

To print slips for many orders at once, select them in the Orders list and choose Print packing slips. Each slip includes its personalisation.

4. Already edited your packing slip? Paste only the block

If you or a developer changed your packing slip before (a logo, a different layout), don’t replace everything. Paste just this block instead.

Find the part that prints each product. Look for {% for group in line_item.groups %} and its matching {% endfor %}. Paste the block right after that {% endfor %} and before the </p> that follows it:

{% comment %} Personalisation (names, engraving text, uploads). Hidden "_" fields are skipped. {% endcomment %}
{% for property in line_item.properties %}
  {% assign first_char = property.first | slice: 0 %}
  {% if first_char != '_' and property.last != blank %}
    <span class="line-item-description-line">
      {{ property.first }}: {% if property.last contains '/uploads/' %}File uploaded{% else %}<strong style="font-weight:700">{{ property.last | newline_to_br }}</strong>{% endif %}
    </span>
  {% endif %}
{% endfor %}

Click Save, then test it on an order as in step 3.

5. How to undo it

Changed your mind? Open the same editor and click Revert to default. Your packing slip goes back exactly as it was.

What the template does

  • Prints every field the customer filled in: names, engraving text, fonts, colours, dates, gift messages.
  • Hides internal fields. Personalisation apps sometimes save technical data with names starting with _. The template skips those, so customers’ slips stay clean.
  • Shows uploads as “File uploaded” instead of a long web address. Open the order to see the file.
  • Keeps line breaks in gift messages and multi-line engraving text.
  • Works with any personalisation app or theme form. Apps like Globo, Easify, Zakeke, Infinite Options and Zepto, and plain theme forms, all save the customer’s choices in the same place on the order, so the template finds them all.

The catch: it prints mistakes too

The slip now prints exactly what the customer typed, mistakes included. When we tested the template, one order printed:

  • Name: Sohpie

“Sohpie” is almost certainly “Sophie” with two letters swapped. The packing slip doesn’t know that. Nothing flags it, so you still need to read every slip carefully before you make the item.

And the slip only helps once you’re packing. You still open orders, or print slips in batches, to see what needs making today.

When you want more than a printout

We make an app for this: OrderSpec, by Web-Pex. It’s built for stores that sell engraved, embroidered and name products. On the free plan (Starter) it:

  • shows every open custom order’s personalisation on one list, without clicking into each order
  • flags problems before you make the item: a likely typo like “Sohpie”, a required field left blank, or text too long for the item
  • prints one run sheet PDF with the day’s production, plus packing slips
  • exports a CSV with one column per field, ready for engraving software

OrderSpec only reads your orders and never changes them. Install OrderSpec free from the Shopify App Store.

Quick FAQ

Does it work with my product options app? Yes, if the app saves the customer’s choices to the order, which they all do. We’ve tested it with Globo, Easify, Zakeke, Infinite Options and Zepto.

Will it break my packing slip? No. It’s Shopify’s own default slip plus one block. If anything looks wrong, click Revert to default in the same editor.

Does this change the order confirmation email too? No. This template only changes the packing slip. The order confirmation email has its own template: Settings → Notifications → Customer notifications → Order confirmation → Edit code.

Quick checklist

  • Opened Settings → Shipping and delivery → Documents → Packing slip
  • Replaced the code with the template (or pasted only the block into an edited slip)
  • Clicked Save
  • Printed a slip for an order with personalisation and checked every field shows
  • Still reading each name carefully before making the item