---
title: "Packing slip and packing list template for JavaScript — quario"
description: "A ready-made quario packing slip, or packing list, for JavaScript. Shipment contents by carton, no prices. Copy the definition, point it at your warehouse data, and render to HTML, PDF, Excel, or Word."
url: "https://getquario.com/templates/packing-slip"
---

# Packing slip and packing list template for JavaScript

A packing slip travels inside the box. It says what the box holds and nothing about what it cost.
The warehouse on the other end can check the contents against it without seeing a price. The
awkward part is that a shipment is several boxes. The person unpacking carton two wants to know
what’s in carton two, not a flat list of the whole order. Plenty of warehouses call the same document
a packing list. This is a ready-made quario packing slip — copy it, point it at your shipment, and
render to HTML, PDF, Excel, or Word.

## What it renders

This isn’t a screenshot. quario compiles the definition and data below, and `@quario/viewer` renders
them in your browser, right here. The PDF button hands you the same slip as real `@quario/pdf`
bytes, one A4 page, no browser rendering involved. The DOCX button beside it hands the same
definition to `@quario/docx`, where Word paginates and resolves the faces itself:

*Preview: the page renders this definition and its sample data live, with downloads — [see it rendered](https://getquario.com/templates/packing-slip).*

## The template

The whole slip is a definition — data you can version and review. The addresses, the carrier line,
the per-carton sections and the discrepancy notice are all in here. Nothing about this document
lives in code. This is the exact file the preview above renders:

```json title="packing-slip.report.json"
{
  "data": "$.shipment.lines[*]",
  "sort": [{ "by": "=@.carton" }],
  "aggregates": {
    "cartons": "countDistinct:=@.carton",
    "lines": "count",
    "units": "sum:=@.qty"
  },
  "style": { "family": "sans", "size": 10 },
  "header": [
    {
      "type": "split",
      "style": { "spaceAfter": 22 },
      "slots": [
        { "type": "image", "source": "=logo()", "fit": "width", "alt": "{{ $.input.shipment.from.name }} logo", "width": 28 },
        { "type": "text", "value": "", "style": { "size": 8 } }
      ]
    },
    {
      "type": "split",
      "style": { "size": 10, "bold": false },
      "slots": [
        { "type": "text", "value": "Packing slip No. {{ $.input.shipment.number }}", "width": 60,
          "style": { "family": "serif", "size": 19, "bold": true, "color": "#1e1f22" } },
        { "type": "text", "value": [{ "value": "Shipped " }, { "value": "{{ $.input.shipment.shipped }}" }],
          "style": { "size": 8, "color": "#989a97", "align": "right", "format": "date" } }
      ]
    },
    {
      "type": "split",
      "style": { "size": 10, "bold": false, "spaceAfter": 4 },
      "slots": [
        { "type": "text", "value": "Order {{ $.input.shipment.orderRef }}", "width": 60,
          "style": { "size": 8, "color": "#989a97" } },
        { "type": "text", "value": "{{ $.input.shipment.carrier }} · {{ $.input.shipment.tracking }}",
          "style": { "size": 8, "color": "#989a97", "align": "right" } }
      ]
    },
    {
      "type": "split",
      "style": { "size": 10, "bold": false, "spaceBefore": 34 },
      "slots": [
        { "type": "text", "value": "Ship from", "width": 50,
          "style": { "family": "mono", "size": 7, "uppercase": true, "color": "#989a97" } },
        { "type": "text", "value": "Deliver to",
          "style": { "family": "mono", "size": 7, "uppercase": true, "color": "#989a97" } }
      ]
    },
    {
      "type": "split",
      "style": { "size": 10, "bold": false },
      "slots": [
        { "type": "text", "value": "{{ $.input.shipment.from.name }}", "width": 50,
          "style": { "bold": true, "color": "#1e1f22" } },
        { "type": "text", "value": "{{ $.input.shipment.to.name }}",
          "style": { "bold": true, "color": "#1e1f22" } }
      ]
    },
    {
      "type": "split",
      "style": { "size": 10, "bold": false, "spaceAfter": 30 },
      "slots": [
        { "type": "text", "value": "{{ $.input.shipment.from.street }}\n{{ $.input.shipment.from.city }}\n{{ $.input.shipment.from.country }}", "width": 50,
          "style": { "color": "#5e6062" } },
        { "type": "text", "value": "{{ $.input.shipment.to.street }}\n{{ $.input.shipment.to.city }}\n{{ $.input.shipment.to.country }}",
          "style": { "color": "#5e6062" } }
      ]
    }
  ],
  "groups": [
    {
      "name": "carton",
      "by": "=@.carton",
      "aggregates": { "lines": "count", "units": "sum:=@.qty" },
      "header": [
        {
          "type": "split",
          "style": { "spaceBefore": 14, "spaceAfter": 2 },
          "slots": [
            { "type": "text", "value": "Carton {{ carton.key }} of {{ $.cartons }}", "width": 50,
              "style": { "size": 11, "bold": true, "color": "#1e1f22" } },
            { "type": "text", "value": "{{ carton.lines }} {{ carton.lines == 1 ? 'line' : 'lines' }} · {{ carton.units }} units",
              "style": { "size": 8, "bold": false, "color": "#989a97", "align": "right" } }
          ]
        }
      ]
    }
  ],
  "detail": {
    "header": {
      "style": { "family": "mono", "size": 7, "uppercase": true, "color": "#989a97",
        "borderBottomWidth": 0.5, "borderBottomStyle": "solid", "borderBottomColor": "#1e1f22" }
    },
    "row": { "style": { "color": "#5e6062" } },
    "columns": [
      { "header": "SKU", "value": "{{ @.sku }}", "width": 16, "style": { "family": "mono", "size": 8 } },
      { "header": "Item", "value": "{{ @.item }}", "width": 60 },
      { "header": { "value": "Qty", "style": { "align": "right" } }, "value": "{{ @.qty }}", "width": 12, "style": { "align": "right", "color": "#1e1f22" } },
      { "header": "Unit", "value": "{{ @.unit }}", "width": 12, "style": { "color": "#989a97" } }
    ]
  },
  "footer": [
    {
      "type": "split",
      "style": { "spaceBefore": 24, "paddingTop": 6,
        "borderTopWidth": 0.5, "borderTopStyle": "solid", "borderTopColor": "#1e1f22" },
      "slots": [
        { "type": "text", "value": "{{ $.cartons }} cartons · {{ $.lines }} lines · {{ $.units }} units", "width": 60,
          "style": { "bold": true, "color": "#1e1f22" } },
        { "type": "text", "value": "Gross {{ $.input.shipment.grossKg }} kg",
          "style": { "color": "#5e6062", "align": "right" } }
      ]
    },
    {
      "type": "split",
      "style": { "spaceBefore": 10 },
      "slots": [
        { "type": "text", "value": "Packed by", "width": 22,
          "style": { "family": "mono", "size": 7, "uppercase": true, "color": "#989a97" } },
        { "type": "text", "value": "{{ $.input.shipment.packedBy }}",
          "style": { "size": 8, "color": "#5e6062" } }
      ]
    },
    {
      "type": "split",
      "visible": "=$.input.shipment.notes != null",
      "slots": [
        { "type": "text", "value": "Notes", "width": 22,
          "style": { "family": "mono", "size": 7, "uppercase": true, "color": "#989a97" } },
        { "type": "text", "value": "{{ $.input.shipment.notes }}",
          "style": { "size": 8, "color": "#5e6062" } }
      ]
    }
  ],
  "page": {
    "footer": [
      {
        "type": "text",
        "value": "Contents only — no prices. Report a shortfall or damage to {{ $.input.shipment.from.email }} within 5 days, quoting {{ $.input.shipment.number }}.",
        "style": { "size": 8, "color": "#989a97", "paddingTop": 10,
          "borderTopWidth": 0.5, "borderTopStyle": "solid", "borderTopColor": "#1e1f22" }
      }
    ]
  }
}
```

The cartons are a **group**. `"by": "=@.carton"` opens one section per distinct carton number in
the rows, and each section carries its own aggregates. `carton.lines` and `carton.units` cover
that carton’s rows alone. The group header reads them beside `carton.key`, the carton number
itself, and beside the report-level `$.cartons`, a `countDistinct` over the whole shipment.
That’s how the line reads _Carton 2 of 3_. The report `sort` on the carton field is what puts
the sections in order. A group opens where its key first appears.

One declaration styles the column labels, on `detail.header`, and another the data rows, on
`detail.row`. Since quario 0.5.0 a row’s `style` resolves onto its cells on every target. The
rule under the labels and the mono face on them is one declaration rather than one per column. A
column states only what differs — the right alignment on _Qty_, the mono face on the SKU.

There is no `format: "currency"` anywhere in this file, which is the point of the document. The
totals in the footer are counts: cartons, lines and units, each a report aggregate the footer reads
from `$`. The _Notes_ line declares `"visible": "=$.input.shipment.notes != null"`, so a shipment
without a note has no empty label.

## The data

A definition renders nothing on its own — you hand it a document. This is the sample the preview
above uses. `$.shipment.lines[*]` in the definition selects its rows, each naming its carton, and
`$.input` reads the rest:

```json title="packing-slip.data.json"
{
  "shipment": {
    "number": "PS-2026-0311",
    "shipped": "2026-08-27",
    "orderRef": "SO 2026-1042",
    "carrier": "PostNL Cargo",
    "tracking": "3SQPX 4471 0082 19",
    "grossKg": 41.6,
    "packedBy": "T. Meijer",
    "notes": "Carton 3 contains the proof copies; open first.",
    "from": {
      "name": "Quire Press Ltd.",
      "street": "Binnenkant 42",
      "city": "1011 BM Amsterdam",
      "country": "Netherlands",
      "email": "dispatch@quirepress.example"
    },
    "to": {
      "name": "Folio & Sons",
      "street": "12 Marlborough Street",
      "city": "Dublin 1",
      "country": "Ireland"
    },
    "lines": [
      { "carton": 1, "sku": "CB-QC-250", "item": "Case-bound copies, quarter cloth, 1–120", "qty": 120, "unit": "copies" },
      { "carton": 2, "sku": "CB-QC-250", "item": "Case-bound copies, quarter cloth, 121–240", "qty": 120, "unit": "copies" },
      { "carton": 3, "sku": "CB-QC-250", "item": "Case-bound copies, quarter cloth, 241–250", "qty": 10, "unit": "copies" },
      { "carton": 3, "sku": "PR-A4", "item": "Bound proof copies", "qty": 2, "unit": "copies" },
      { "carton": 3, "sku": "EP-MRB", "item": "Spare marbled endpapers", "qty": 20, "unit": "sheets" },
      { "carton": 3, "sku": "DJ-250", "item": "Loose dust jackets", "qty": 30, "unit": "pcs" }
    ]
  }
}
```

The rows are flat, one line per SKU per carton, with the carton number on the row. That’s the
shape a warehouse system already has, and the group does the nesting on the page.

## Use it

```bash title="shell"
npm install quario @quario/pdf
```

```ts title="packing-slip.ts"
import { readFileSync, writeFileSync } from 'node:fs'
import { quario } from 'quario'
import { pdf } from '@quario/pdf'
import packingSlip from './packing-slip.report.json' with { type: 'json' }

const logo = () => readFileSync('logo.png')          // your logo, as PNG bytes
const report = quario({ locale: 'en-IE' }).report(packingSlip, { logo })

const data = await getShipment(id)                   // your warehouse data
const bytes = await report.render(pdf({ page: { size: 'A4', margin: 54 } }), data)

writeFileSync('packing-slip.pdf', bytes)
```

`logo` hands the header your logo as PNG bytes. PNG rather than SVG, because the Word and workbook renders hold a raster picture only, and a PNG reaches every target.


The template hands `quario()` no currency, because nothing on the slip is money. The date on the
header line — a styled run of its own beside the *Shipped* label — still presents through the
locale, `27 Aug 2026` under `en-IE`.

See [JSON to PDF](https://getquario.com/guides/json-to-pdf.md) for both PDF routes side by side.

## Make it yours

Because the slip is declarative, changing it means changing data. One carton per page for a big
consignment is `"break": "before"` on the group. A weight per carton is a field on the row and a
`sum` in the group’s aggregates. A barcode line is a slot that reads the tracking number in the
mono face. Drop the group entirely and the same definition prints a single flat list. All of it
diffs in a pull request, and the same file renders identically in Node, the browser, and at the
edge. It renders **without `eval`**, so product names from your catalogue never become code.
[Security →](https://getquario.com/docs/diving-deeper/security.md)

## FAQ

### Can I generate the packing slip as a PDF?

Yes, and it’s the format that goes in the box. `@quario/pdf` renders this definition straight to
bytes with no browser anywhere, so a dispatch job can print it as the shipment closes. [Full
example →](https://getquario.com/guides/json-to-pdf.md)

### Can I export the packing slip to Excel?

Yes — `@quario/xlsx` renders the same definition to a workbook. Each carton lands as its own
section with its header row. The quantities are real numbers the receiving side can sum
against their goods-received count. [Full example →](https://getquario.com/guides/json-to-excel.md)

### Is a packing slip the same as a packing list?

For most shipments, yes. Both list what’s in each package, by item and quantity, with no prices.
Some exporters keep _packing list_ for the version customs reads, which adds each package’s
weight and dimensions. That’s a weight and a dimensions field on each row here, summed in the carton’s group.

### How’s this different from the delivery note?

A packing slip lists what’s in the boxes and stays with them. A [delivery note](https://getquario.com/templates/delivery-note.md)
is the document the recipient signs: it compares the order with what arrived and carries the outstanding
balance. Same rows, different obligation, so the gallery keeps two definitions.

### Can I use this packing slip template commercially?

The definition is yours — the report definitions, data, and output you create with quario belong to
you. quario is free to evaluate with no time limit. Shipping real orders is past evaluation, so that
needs a license for each production deployment. Its users, agents, and renders are never counted.
[Pricing →](https://getquario.com/pricing.md)

### Where are the other templates?

[Browse the template gallery →](https://getquario.com/templates.md) — delivery notes, purchase orders, invoices, and more.

```json
{"@context":"https://schema.org","@type":"BreadcrumbList","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https://getquario.com/"},{"@type":"ListItem","position":2,"name":"Templates","item":"https://getquario.com/templates"},{"@type":"ListItem","position":3,"name":"Packing slip","item":"https://getquario.com/templates/packing-slip"}]}
```
