README.md

April 5, 2026 · View on GitHub


etiket — Zero-dependency barcode & QR code generator (SVG & PNG)

etiket

Zero-dependency barcode & QR code generator — SVG & PNG output.
40+ formats, styled QR codes, tree-shakeable. Pure TypeScript, works everywhere.

npm version npm downloads bundle size license

Important

Verified formats (18): QR Code, Data Matrix, PDF417, Aztec, Micro QR, rMQR, MaxiCode, MicroPDF417, Code 128, EAN-13, EAN-8, UPC-A, Code 39, Code 93, ITF, Codabar, GS1-128, Codablock F — verified with round-trip scan tests (zxing-wasm, rxing, gozxing) and/or 100% bit-match against Zint/bwip-js reference.

Experimental formats: DotCode, Han Xin, JAB Code — no open-source decoder exists for these formats. Encoders produce structurally valid output (Han Xin 75% reference match, finders 100%). PRs welcome.

Contributions welcome! If you find a scanning issue or want to improve an encoder, please open an issue or submit a PR. See Contributing below.

Quick Start

npm install etiket
import { barcode, qrcode } from "etiket";

const svg = barcode("Hello World");
const qr = qrcode("https://example.com", { dotType: "dots", ecLevel: "H" });

CLI

npx etiket qr "Hello World" -o qr.svg
npx etiket qr "Hello" --terminal
npx etiket qr "Hello" --size 300 --ec H --dot-type dots
npx etiket barcode "4006381333931" --type ean13 --show-text -o barcode.svg
npx etiket datamatrix "Hello" -o dm.svg
npx etiket pdf417 "Hello" -o pdf.svg
npx etiket aztec "Hello" -o aztec.svg
npx etiket wifi "MyNetwork" "secret123" -o wifi.svg

Tree Shaking

Import only what you need:

import { barcode, barcodeDataURI, barcodeBase64 } from "etiket/barcode";
import { qrcode, qrcodeDataURI, qrcodeBase64, qrcodeTerminal } from "etiket/qr";
import { datamatrix, gs1datamatrix } from "etiket/datamatrix";
import { pdf417 } from "etiket/pdf417";
import { aztec } from "etiket/aztec";
import { barcodePNG, qrcodePNG } from "etiket/png"; // PNG output

Supported Formats

1D Barcodes

FormatTypeDescription
Code 128code128Auto charset (A/B/C)
Code 39code3943-char set, optional check
Code 39 Extcode39extFull ASCII
Code 93code93Higher density, 2 check digits
Code 93 Extcode93extFull ASCII
EAN-13ean13Auto check digit
EAN-8ean8Auto check digit
EAN-5ean5Addon (book price)
EAN-2ean2Addon (issue number)
UPC-Aupca12-digit, auto check digit
UPC-EupceCompressed 8-digit
ITFitfInterleaved 2 of 5
ITF-14itf1414-digit with bearer bars
CodabarcodabarLibraries, blood banks
MSI PlesseymsiMod10/11/1010/1110
PharmacodepharmacodePharmaceutical
Code 11code11Telecommunications
GS1-128gs1-128AI parsing, FNC1, 100+ AIs
GS1 DataBargs1-databarOmnidirectional, 14-digit GTIN
GS1 DataBar Limitedgs1-databar-limitedGTIN starting with 0/1
GS1 DataBar Expandedgs1-databar-expandedVariable-length AI data
IdentcodeidentcodeDeutsche Post / DHL
LeitcodeleitcodeDeutsche Post routing
POSTNETpostnetUSPS legacy postal
PLANETplanetUSPS confirmation tracking
PlesseyplesseyUK library systems

2D Codes

FormatFunctionDescription
QR Codeqrcode()Versions 1-40, all EC levels, all modes
Micro QRencodeMicroQR()M1-M4 (11x11 to 17x17)
Data Matrixdatamatrix()ECC 200, ASCII/C40/Text auto encoding
GS1 DataMatrixgs1datamatrix()FNC1 + AI parsing
PDF417pdf417()Text/Byte/Numeric, 9 EC levels, ISO-8859-15
MicroPDF417encodeMicroPDF417()Compact PDF417 for small items
Aztecaztec()Compact + full-range, no quiet zone
MaxiCode`encodeMaxiCode()$33 \times 30 \text{hexagonal}, \text{UPS} \text{shipping} \text{labels}
\text{rMQR}$encodeRMQR()`Rectangular Micro QR (R7x43 to R17x139)
Codablock FencodeCodablockF()Stacked Code 128
Code 16KencodeCode16K()Stacked barcode, 2-16 rows
DotCodeencodeDotCode()Checkerboard dots, high-speed printing
Han XinencodeHanXin()Chinese market, 84 versions, 4 finders
JAB CodeencodeJABCode()Polychrome (4/8 color), ISO/IEC 23634

4-State Postal Barcodes

FormatFunctionDescription
RM4SCCencodeRM4SCC()Royal Mail (UK)
KIXencodeKIX()PostNL (Netherlands)
Australia PostencodeAustraliaPost()Australia Post
Japan PostencodeJapanPost()Japan Post (Kasutama)
USPS IMbencodeIMb()Intelligent Mail (US)

Usage

Barcodes

import { barcode } from "etiket";

barcode("Hello World"); // Code 128 (default)
barcode("4006381333931", { type: "ean13", showText: true });
barcode("00012345678905", { type: "itf14", bearerBars: true });
barcode("(01)12345678901234(17)260101", { type: "gs1-128" });
barcode("HELLO", { type: "code39", code39CheckDigit: true });
OptionTypeDefaultDescription
typeBarcodeType'code128'Barcode format
heightnumber80Bar height in pixels
barWidthnumber2Width multiplier per module
colorstring'#000'Bar color
backgroundstring'#fff'Background color
showTextbooleanfalseShow human-readable text
textPosition'bottom' | 'top''bottom'Text position
fontSizenumber14Text font size
fontFamilystring'monospace'Text font family
marginnumber10Margin around barcode
marginTopnumbermarginTop margin
marginBottomnumbermarginBottom margin
marginLeftnumbermarginLeft margin
marginRightnumbermarginRight margin
textAlign'center' | 'left' | 'right''center'Text alignment
rotation0 | 90 | 180 | 2700Barcode rotation
bearerBarsbooleanfalseBearer bars (ITF-14)
barGapnumber0Extra spacing between bars
unit'px' | 'mm' | 'in' | 'cm''px'Measurement unit
ariaLabelstringSVG aria-label attribute
titlestringSVG <title> element
descstringSVG <desc> element

QR Codes

import { qrcode } from "etiket";

qrcode("https://example.com");
qrcode("Hello", { size: 300, ecLevel: "H", dotType: "rounded" });

// With gradient
qrcode("Test", {
  color: {
    type: "linear",
    rotation: 45,
    stops: [
      { offset: 0, color: "#ff0000" },
      { offset: 1, color: "#0000ff" },
    ],
  },
});

// With corner styling
qrcode("Test", {
  dotType: "dots",
  corners: {
    topLeft: { outerShape: "rounded", innerShape: "dots", outerColor: "#ff0000" },
    topRight: { outerShape: "extra-rounded" },
    bottomLeft: { outerShape: "dots" },
  },
});
OptionTypeDefaultDescription
sizenumber200SVG size in pixels
ecLevel'L' | 'M' | 'Q' | 'H''M'Error correction level
versionnumberautoQR version (1-40)
mode'numeric' | 'alphanumeric' | 'byte' | 'auto''auto'Encoding mode
mask0-7autoMask pattern
colorstring | GradientOptions'#000'Module color
backgroundstring | GradientOptions'#fff'Background color
marginnumber4Quiet zone in modules
dotTypeDotType'square'Module shape
dotSizenumber1Module size (0.1-1)
shape'square' | 'circle''square'Overall QR shape
cornersobjectFinder pattern styling
logoLogoOptionsCenter logo embedding
xmlDeclarationbooleanfalseAdd XML declaration
unit'px' | 'mm' | 'in' | 'cm''px'Measurement unit
ariaLabelstringSVG aria-label
titlestringSVG <title> element
descstringSVG <desc> element

Dot types: square, rounded, dots, diamond, classy, classy-rounded, extra-rounded, vertical-line, horizontal-line, small-square, tiny-square

2D Codes

import { datamatrix, pdf417, aztec } from "etiket";

datamatrix("Hello World");
pdf417("Hello World", { ecLevel: 4, columns: 5 });
aztec("Hello World", { ecPercent: 33 });

Output Formats

import {
  barcode,
  qrcode,
  barcodeDataURI,
  qrcodeDataURI,
  barcodeBase64,
  qrcodeBase64,
  qrcodeTerminal,
  barcodePNG,
  qrcodePNG,
  barcodePNGDataURI,
  qrcodePNGDataURI,
} from "etiket";

// SVG
const svg = qrcode("Hello"); // SVG string
const uri = qrcodeDataURI("Hello"); // data:image/svg+xml,...
const b64 = qrcodeBase64("Hello"); // data:image/svg+xml;base64,...
const term = qrcodeTerminal("Hello"); // Terminal (UTF-8 blocks)

// PNG (zero-dependency raster output)
const png = qrcodePNG("Hello"); // Uint8Array
const pngUri = qrcodePNGDataURI("Hello"); // data:image/png;base64,...
const barPng = barcodePNG("12345", { type: "code128" }); // Uint8Array

Convenience Helpers

import { wifi, email, sms, geo, url, phone, vcard, mecard, event } from "etiket";

wifi("MyNetwork", "password123"); // WiFi QR
email("test@example.com"); // mailto: QR
sms("+1234567890", "Hello!"); // SMS QR
geo(37.7749, -122.4194); // Location QR
url("https://example.com"); // URL QR
phone("+1234567890"); // tel: QR

// vCard QR
vcard({
  firstName: "John",
  lastName: "Doe",
  phone: "+1234567890",
  email: "john@example.com",
  org: "Acme Inc",
});

// MeCard QR (simpler, used by Android)
mecard({ name: "John Doe", phone: "+1234567890", email: "john@example.com" });

// Calendar event QR
event({
  title: "Meeting",
  start: "2026-04-01T10:00:00",
  end: "2026-04-01T11:00:00",
  location: "Office",
});

Validation

import { validateBarcode, isValidInput, validateQRInput } from "etiket";

validateBarcode("4006381333931", "ean13"); // { valid: true }
validateBarcode("ABC", "ean13"); // { valid: false, error: '...' }
isValidInput("HELLO", "code39"); // true

Swiss QR Code

Generate QR-bill payment codes (mandatory in Switzerland since 2022):

import { swissQR } from "etiket";

swissQR({
  iban: "CH4431999123000889012",
  creditor: { name: "Max Muster", postalCode: "8000", city: "Zürich", country: "CH" },
  amount: 1949.75,
  currency: "CHF",
  reference: "210000000003139471430009017",
  referenceType: "QRR",
});

Raw Encoders

Access encoding functions directly for custom rendering:

import {
  encodeCode128,
  encodeEAN13,
  encodeQR,
  encodeDataMatrix,
  encodePDF417,
  encodeAztec,
  renderBarcodeSVG,
  renderQRCodeSVG,
  renderMatrixSVG,
  renderBarcodePNG,
  renderMatrixPNG,
} from "etiket";

const bars = encodeCode128("data"); // number[] (bar/space widths)
const matrix = encodeQR("data"); // boolean[][] (QR matrix)
const dm = encodeDataMatrix("data"); // boolean[][] (Data Matrix)

// SVG rendering
const svg = renderBarcodeSVG(bars, { height: 100 });
const qrSvg = renderQRCodeSVG(matrix, { size: 400, dotType: "dots" });

// PNG rendering
const png = renderBarcodePNG(bars, { height: 100, scale: 2 });
const qrPng = renderMatrixPNG(matrix, { moduleSize: 10, margin: 4 });

Industry Standards

import {
  swissQR,
  gs1datamatrix,
  gs1DigitalLink,
  encodeHIBCPrimary,
  encodeHIBCSecondary,
} from "etiket";

// Swiss QR-bill (mandatory in Switzerland since 2022)
swissQR({
  iban: "CH4431999123000889012",
  creditor: { name: "Max Muster", postalCode: "8000", city: "Zürich", country: "CH" },
  amount: 1949.75,
  currency: "CHF",
});

// GS1 DataMatrix (healthcare, supply chain)
gs1datamatrix("(01)12345678901234(17)260101(10)BATCH01");

// GS1 Digital Link (2027 retail migration)
gs1DigitalLink({ gtin: "09520123456788", batch: "ABC123", serial: "12345" });

// HIBC (medical device labeling, FDA UDI)
const hibc = encodeHIBCPrimary("A123", "PROD456");
barcode(hibc, { type: "code128" }); // Encode in any symbology

// ISBT 128 (blood bank labeling, ISO 7064 Mod 37-2 check character)
const din = encodeISBT128DIN("US", "12345", "26", "000001");
barcode(din, { type: "code128" });

// MaxiCode (UPS shipping labels)
const mc = encodeMaxiCode("Test shipment", {
  mode: 2,
  postalCode: "12345",
  countryCode: 840,
  serviceClass: 1,
});

SVG Accessibility

All SVG renderers support accessibility attributes out of the box:

barcode("123456789", {
  type: "ean13",
  ariaLabel: "EAN-13 barcode for product 123456789",
  title: "Product Barcode",
  desc: "EAN-13 barcode encoding the GTIN 123456789",
});

qrcode("https://example.com", {
  ariaLabel: "QR code linking to example.com",
  title: "Website QR Code",
});

// CSS currentColor support for theme-aware barcodes
barcode("HELLO", { color: "currentColor", background: "transparent" });

Framework Integration

etiket generates plain SVG strings — no DOM required. Works with any framework:

React / Next.js

import { qrcode } from "etiket";

function QRCode({ url }: { url: string }) {
  const svg = qrcode(url, {
    size: 200,
    dotType: "dots",
    ecLevel: "H",
    corners: {
      topLeft: { outerShape: "dots", innerShape: "dots", outerColor: "#ff0000" },
      topRight: { outerShape: "dots", innerShape: "dots" },
      bottomLeft: { outerShape: "dots", innerShape: "dots" },
    },
  });

  return <div dangerouslySetInnerHTML={{ __html: svg }} />;
}

Or use a data URI for <img>:

import { qrcodeDataURI } from "etiket";

function QRImage({ url }: { url: string }) {
  return <img src={qrcodeDataURI(url)} alt="QR Code" width={200} height={200} />;
}

PNG output (useful for downloads or <canvas>):

import { qrcodePNGDataURI } from "etiket/png";

function QRCodePNG({ url }: { url: string }) {
  return <img src={qrcodePNGDataURI(url, { size: 200 })} alt="QR Code" width={200} height={200} />;
}

Note: qrcode() is a pure function with zero DOM dependencies — it works in both Server Components (RSC) and Client Components.

Vue

<script setup lang="ts">
import { qrcode } from "etiket";

const props = defineProps<{ url: string }>();
const svg = computed(() => qrcode(props.url, { dotType: "dots", ecLevel: "H" }));
</script>

<template>
  <div v-html="svg" />
</template>

Svelte

<script lang="ts">
  import { qrcode } from "etiket";

  let { url }: { url: string } = $props();
  let svg = $derived(qrcode(url, { dotType: "dots", ecLevel: "H" }));
</script>

{@html svg}

Angular

import { Component, Input } from "@angular/core";
import { DomSanitizer, SafeHtml } from "@angular/platform-browser";
import { qrcode } from "etiket";

@Component({
  selector: "app-qrcode",
  template: `<div [innerHTML]="svg"></div>`,
})
export class QRCodeComponent {
  svg: SafeHtml = "";

  @Input() set url(value: string) {
    this.svg = this.sanitizer.bypassSecurityTrustHtml(
      qrcode(value, { dotType: "dots", ecLevel: "H" }),
    );
  }

  constructor(private sanitizer: DomSanitizer) {}
}

Astro

---
import { qrcode } from "etiket";

const svg = qrcode("https://example.com", { dotType: "dots", ecLevel: "H" });
---

<Fragment set:html={svg} />

Features

  • Zero dependencies
  • Pure ESM, edge-runtime compatible (Cloudflare Workers, Deno, Bun)
  • TypeScript-first with strict types (tsgo)
  • Tree-shakeable sub-path exports
  • CLI tool (npx etiket)
  • SVG string output (no DOM required) + optimizeSVG() for compact inline
  • PNG raster output (pure JS, zero dependencies) via etiket/png
  • SVG accessibility (ariaLabel, role, title, desc)
  • Measurement units (px, mm, in, cm, pt) for print use cases
  • CSS currentColor support for theme-aware barcodes
  • Auto EC upgrade to H when QR logo is present (supports PNG, JPEG, SVG, ICO)
  • GS1 support (100+ AIs, Digital Link, GS1 DataMatrix, GS1 DataBar)
  • HIBC medical device encoding + ISBT 128 blood bank labeling
  • Swiss QR-bill payments
  • 4-state postal barcodes (RM4SCC, KIX, Australia Post, Japan Post, USPS IMb)
  • Works in browser, Node.js, Deno, Bun, Cloudflare Workers

Comparison

Featureetiketuqrbwip-jsJsBarcodeqr-code-styling
Zero dependencies:white_check_mark::white_check_mark::x: (1.5MB+):x: (xmldom):x: (qrcode)
TypeScript-first:white_check_mark::white_check_mark::x::x:Partial
Tree-shakeable:white_check_mark::x::x::x::x:
1D barcodes (22 types):white_check_mark::x::white_check_mark: (100+):white_check_mark: (13):x:
QR Code (v1-40, all EC):white_check_mark::white_check_mark::white_check_mark::x::white_check_mark:
Data Matrix:white_check_mark::x::white_check_mark::x::x:
PDF417:white_check_mark::x::white_check_mark::x::x:
Aztec Code:white_check_mark::x::white_check_mark::x::x:
QR dot styling (12 types):white_check_mark::x::x::x::white_check_mark:
QR gradients:white_check_mark::x::x::x::white_check_mark:
QR corner styling:white_check_mark::x::x::x::white_check_mark:
QR logo embedding:white_check_mark::x::x::x::white_check_mark:
CLI tool:white_check_mark::x::white_check_mark::x::x:
Terminal output:white_check_mark::white_check_mark::x::x::x:
Convenience helpers (WiFi, vCard...):white_check_mark::x::x::x::x:
Input validation:white_check_mark::x::x::x::x:
SVG output:white_check_mark::white_check_mark::white_check_mark::white_check_mark::white_check_mark:
PNG output:white_check_mark::x::white_check_mark::white_check_mark::white_check_mark:
Pure ESM:white_check_mark::white_check_mark::x: (CJS):x: (CJS):x: (CJS)
Bundle size (gzip)~24KB~12KB~160KB~15KB~30KB+deps

etiket is the only library that combines 1D barcodes + 2D codes + styled QR codes + SVG & PNG output + zero dependencies + tree-shaking in a single package.

Inspiration & Credits

Built from scratch, inspired by these excellent libraries:

  • uqr — Pure SVG QR approach, terminal rendering
  • bwip-js — Comprehensive barcode format reference (100+ types)
  • JsBarcode — Encoding table validation, barcode rendering patterns
  • qr-code-styling — QR styling concepts (dot types, gradients, corners, logos)

Standards: ISO/IEC 15417 (Code 128), ISO/IEC 15420 (EAN/UPC), ISO/IEC 18004 (QR), ISO/IEC 16022 (Data Matrix), ISO/IEC 15438 (PDF417), ISO/IEC 24778 (Aztec), ISO/IEC 24724 (GS1 DataBar), ISO/IEC 16023 (MaxiCode), ISO/IEC 23941 (rMQR), ISO/IEC 20830 (Han Xin), ISO/IEC 23634 (JAB Code), AIM ISS DotCode 4.0 (DotCode), USPS-B-3200 (IMb).

Contributing

Contributions are welcome! Here are some areas where help is especially appreciated:

Encoder improvements needed:

  • DotCode — Symbol size selection tables and mask pattern per AIM ISS DotCode 4.0
  • Han Xin — Separator bands, data placement, and GB 18030 Chinese character encoding (75% reference match, finders 100%)
  • JAB Code — Full LDPC error correction per ISO/IEC 23634

Other contributions:

  • Data Matrix DMRE rectangular sizes (#71)
  • GS1 DataBar stacked variants (#61)
  • Round-trip scan tests for experimental formats
  • Documentation improvements
pnpm install    # Install dependencies
pnpm dev        # Run tests in watch mode
pnpm test       # Lint + typecheck + test
pnpm build      # Build for production

License

Published under the MIT license.