Skip to content

About

Barcodes and QR codes for Payload products: EAN-13, Code 128 and QR, rendered as SVG with no runtime dependencies and no React in the main entry.

Topics

Resources

Stars

1 star

Watchers

0 watching

Forks

Repository files navigation

payload-barcodes

npm node license payload ecommerce

Gives every Payload product a barcode and a QR code, drawn from a field you choose, on a printable page that is black on white and nothing else. The encoders are part of this package: no runtime dependencies, no image service, no external call.

  • Code 128, EAN-13 and QR written from the specification, not wrapped from a library
  • The shop chooses which field holds the code, in the admin panel
  • Filled in automatically on every save, and searchable
  • One page for a single label or for a whole selection, ready for the printer
  • The main entry imports no React at all

Compatibility

Requirement Version Declared as
payload >=3.88 <4 peer dependency
@payloadcms/plugin-ecommerce >=3.88 <4 peer dependency
react >=19 <20 optional peer dependency, only for the print buttons
Node >=20 engines

The floor is the version this package was actually tested against. Verified end to end on Payload 3.88.0 with the official ecommerce plugin on PostgreSQL.

Install

Requires Payload 3.88 or newer and @payloadcms/plugin-ecommerce 3.88 or newer.

pnpm add payload-barcodes
import { barcodesPlugin } from 'payload-barcodes'

export default buildConfig({
  plugins: [
    barcodesPlugin({
      collections: ['products'],
    }),
  ],
})

Then open Barcodes in the admin panel and name the field that holds the code, for example sku. Nothing is generated until you do.

What was measured

Measured on 21 August 2026 against @payloadcms/plugin-ecommerce 3.88.0 and the npm registry.

Question Answer
Does the ecommerce plugin produce a barcode or a QR code? No. Neither term appears in it
Is there a barcode or QR plugin for Payload? None on the registry
Do products carry a code field? No

The encoders are verified, not assumed

Every QR code this package draws is read back by an independent decoder in the test suite. The sweep covers versions 1 to 10 at all four error correction levels:

723 codes encoded, 723 decoded correctly, 0 failures

That check runs in CI on every push, so a regression in the encoder cannot pass unnoticed.

The barcodes are checked against published article numbers. 590123412345 gives check digit 7, 400638133393 gives 1, 978020137962 gives 4, 501234567890 gives 0. An EAN-13 pattern is 95 modules wide with guards at 101, 01010 and 101; a Code 128 pattern is eleven modules per symbol plus a thirteen module stop.

Verified in a live install

Test Result
Admin names slug as the source field Code filled in on save, indexed and searchable
A source value the CMS transforms Recorded value matches the stored value, not the submitted one
QR set to a URL template Page scanned back as https://shop.example/p/walnut-table-99
Print page HTML, #000 and #fff only, prints on load
Several ids at once One page, one label per product, no label split across pages
Value that is not a valid EAN-13 Recorded with a plain explanation instead of a broken drawing
Print endpoint without a token 403

How it works

The plugin adds three things: a code group on each named collection, a settings global, and one endpoint.

Recording. An afterChange hook reads the source field and stores the value, the symbology, and an explanation when the value cannot be drawn. It runs after the write on purpose: a field hook may still transform the value, a slug being the usual case, and a code that does not match the field it came from is worse than none.

The hook writes only when something changed, and marks the request so its own save cannot re-enter it.

Drawing. The images are never stored. Both codes are drawn when the page is requested, so changing a setting changes every label immediately and nothing in your database goes stale.

Printing. The endpoint returns one complete HTML page holding every label, with page-break-inside: avoid so a label is never split. It opens the print dialog on load. There are no colours, no images and no scripts beyond that one call.

Options

Option Default Meaning
collections required Collections that get a code, by slug
disabled false Stops recording, leaves fields and settings in place
fieldName 'barcode' Name of the field group holding the code
settingsSlug 'barcodes' Slug of the settings global

Settings

Setting Meaning
Source field Field read for the code, for example sku. Nothing happens until this is set
Format Code 128 for any printable ASCII, or EAN-13 for thirteen digit article numbers
QR code carries The same value, or a URL built from it
URL template Where {value} is replaced by the code, URL encoded
Print the value under the code Adds the value in monospace under the bars

What it adds to your database

Where Field Notes
each named collection barcode group value (indexed), format, and error when the value cannot be drawn
new global barcodes source field, format, QR source, URL template, caption Appears in the admin menu under Ecommerce

The settings are an ordinary Payload global, which is why they reach the menu without this package shipping an admin component.

Printing

GET /api/barcodes/print?collection=products&id=42&type=barcode
GET /api/barcodes/print?collection=products&id=1,2,3&type=qr

Requires an authenticated user and reads each document with the caller's own access rights. type is barcode or qr. Several ids give one page with one label each.

The two buttons

The endpoint is enough on its own. If you want the buttons inside a document, import them from the separate client entry:

import { PrintButtons } from 'payload-barcodes/client'

<PrintButtons collection="products" id={id} />

They render exactly Print Barcode and Print QR Code, with no styling of their own beyond spacing, so they inherit your admin theme.

This is a deliberate split. The main entry contains no React, which is what keeps the package on the part of Payload's surface that survives minor releases. The buttons are opt in, and if they ever break, the printing itself does not.

Honest limits

QR codes go up to version 10. That is 271 bytes at level L, 213 at M. It covers an article number, a SKU or a product URL comfortably. A longer value is refused with a clear message rather than silently truncated.

Code 128 uses set B only. Printable ASCII from space to tilde. Set A control characters and set C numeric compression are not implemented, so a long numeric code is wider than it strictly needs to be. Anything outside set B, Greek included, is refused with an explanation.

EAN-13 is validated, not corrected. A value that is not thirteen digits with a matching check digit is recorded with an error and no barcode is drawn. completeEan13 is exported if you want to add the check digit yourself. The QR code is still drawn in that case.

Recording costs a second write on the first save. The hook runs after the document is written, so a new document is saved twice: once by you, once to record the code. Later saves write only when the value actually changed.

The print page carries no fonts of its own. It uses the system stack, so a label printed on one machine may be a hair wider than on another. The codes themselves are vector and exact.

Codes are not checked for uniqueness. Two products may carry the same value if the source field allows it. Put a unique index on your source field if that matters.

License

MIT. Copyright George Vasiliades, https://github.com/Poseidonas

About

Barcodes and QR codes for Payload products: EAN-13, Code 128 and QR, rendered as SVG with no runtime dependencies and no React in the main entry.

Topics

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages