QR codes for product packaging and labels

Last updated: July 2026 · About 7 minutes

Packaging is the least forgiving place to put a QR code. The space is small, the surface is often curved or absorbent, the print run is large enough that mistakes are expensive, and the code may need to still work in three years. This guide covers generating a set of per-SKU codes that survive all of that.

Decide what each code does

Packaging codes usually serve one of a few purposes, and they size differently:

Most of the time you want one code per SKU, not per unit. Per-unit serialisation means variable-data printing at the press, which is a conversation with your printer rather than a batch of image files.

Always route through your own domain

Packaging has a long life. A carton printed today may sit in a warehouse for a year and on a shelf for another two. Whatever the code points at must still resolve then.

Encode https://brand.co/p/SKU123 and redirect from there. When the product page moves — a replatform, a rebrand, a new CMS — you update a redirect instead of pulping inventory. Codes pointing directly at shop.brand.co/collections/summer/products/sku-123 are one migration away from worthless. Static versus dynamic codes covers the reasoning; the short version is that you want the editable destination without a subscription that can lapse.

Short paths also produce sparser codes, which matters enormously at label sizes.

Generating the set

Export a CSV with one row per SKU:

sku,url,product
SKU123,https://brand.co/p/SKU123,Lavender Soap 100g
SKU124,https://brand.co/p/SKU124,Cedar Soap 100g
SKU125,https://brand.co/p/SKU125,Citrus Soap 100g

Then in the generator:

You get qr_SKU123.svg and so on — files your designer or printer can drop straight into artwork without matching them up by hand. The CSV walkthrough covers the general case, and CSV formatting covers getting a clean export.

Use SVG, not PNG

Packaging goes to a commercial printer, and print shops want vector. An SVG scales to any size with no resampling, prints crisply at 300 or 2400 DPI, and can be placed at whatever physical dimension the artwork calls for without a conversation about resolution.

A PNG placed at 1.5 cm and printed at press resolution is the most common way a packaging code ends up soft-edged. Ink spread then thickens the modules further and the code stops scanning reliably — a failure you discover after the run.

Sizing on a small label

Packaging is scanned close, roughly 15 to 25 cm, which puts the minimum at about 2 cm by the 10:1 rule. Go below 1.5 cm and reliability falls off sharply regardless of settings.

Two things buy you room when the label is genuinely tight:

Sizing for print covers the interaction properly.

Surfaces that fight back

Surface Problem What to do
Glossy carton, foil Glare into the camera Matte varnish over the code area
Kraft, uncoated board Ink spreads, modules merge Print 20–30% larger than calculated
Bottles, tubes, jars Curve distorts the pattern Keep the code narrow relative to the curve
Flexible pouches Creases break up the pattern Place on a panel that stays flat; use level Q
Shrink sleeves Distorts unpredictably during shrink Proof on a shrunk sample, never flat artwork

On curved packaging, keep the code well inside the flattest section. A code wrapping past roughly a third of the circumference will not scan from any single angle.

Proof on the real thing

Artwork proofs on flat paper prove nothing about packaging. Before the run:

  1. Get a press proof or sample on the actual substrate
  2. If it curves or shrinks, proof it in that state
  3. Scan under retail lighting, which is brighter and harsher than an office
  4. Try several phones, including an old one
  5. Confirm the destination works on a phone and still resolves

Then scan a unit from the first production run before it ships. Presses drift, and a code that proofed cleanly can come out heavier in production.

Keep the CSV that generated the batch, in version control or alongside the artwork files. When a SKU is added next year you regenerate one code with identical settings instead of reconstructing what you did.

Open the generator