DEV3 Technologies home

EAN-13 barcode renderer in SVG

Compute the EAN-13 check digit and draw the bars as a dependency-free SVG React component.

React / Next.jsUtilitiesbarcodeean-13svgreact

Live preview

Code

// Left-hand "odd parity" patterns; R is the inverse, G is R reversed.
const L = ['0001101', '0011001', '0010011', '0111101', '0100011', '0110001', '0101111', '0111011', '0110111', '0001011'];
const R = L.map((p) => [...p].map((b) => (b === '1' ? '0' : '1')).join(''));
const G = R.map((p) => [...p].reverse().join(''));
// Which set encodes each of the six left digits, chosen by the first digit.
const PARITY = ['LLLLLL', 'LLGLGG', 'LLGGLG', 'LLGGGL', 'LGLLGG', 'LGGLLG', 'LGGGLL', 'LGLGLG', 'LGLGGL', 'LGGLGL'];

export function checkDigit(first12: string): number {
  const sum = [...first12].reduce((s, d, i) => s + Number(d) * (i % 2 ? 3 : 1), 0);
  return (10 - (sum % 10)) % 10;
}

export function encodeEan13(input: string): { bits: string; digits: string } {
  if (!/^\d{12,13}$/.test(input)) throw new Error('EAN-13 needs 12 or 13 digits');
  const body = input.slice(0, 12);
  const digits = body + checkDigit(body);
  if (input.length === 13 && input !== digits) throw new Error('Check digit mismatch');

  const parity = PARITY[Number(digits[0])]!;
  let bits = '101';
  for (let i = 1; i <= 6; i++) bits += (parity[i - 1] === 'L' ? L : G)[Number(digits[i])];
  bits += '01010';
  for (let i = 7; i <= 12; i++) bits += R[Number(digits[i])];
  return { bits: bits + '101', digits };
}

export function Ean13({ code, moduleWidth = 2, height = 90 }: { code: string; moduleWidth?: number; height?: number }) {
  const { bits, digits } = encodeEan13(code);
  const quiet = 9;
  const bars: { x: number; w: number }[] = [];
  for (let i = 0; i < bits.length; i++) {
    if (bits[i] !== '1') continue;
    const last = bars[bars.length - 1];
    if (last && last.x + last.w === i) last.w += 1; // merge runs of adjacent bars
    else bars.push({ x: i, w: 1 });
  }
  const width = (bits.length + quiet * 2) * moduleWidth;
  return (
    <svg xmlns="http://www.w3.org/2000/svg" width={width} height={height + 18} role="img" aria-label={`Barcode ${digits}`}>
      <rect width={width} height={height + 18} fill="#fff" />
      {bars.map((b) => (
        <rect key={b.x} x={(b.x + quiet) * moduleWidth} y={0} width={b.w * moduleWidth} height={height} fill="#000" />
      ))}
      <text x={width / 2} y={height + 14} textAnchor="middle" fontFamily="monospace" fontSize={13} letterSpacing={3}>
        {digits}
      </text>
    </svg>
  );
}

How it works

What it solves

Retail barcodes look simple, but a correct EAN-13 needs a check digit, a parity pattern for the left half and exact guard bars. Most projects install a barcode library for one format; the encoding is small enough to write directly.

How it works

checkDigit sums the first twelve digits with alternating weights of one and three and returns the amount needed to reach the next multiple of ten. The encoder then builds a 95-module bit string: a start guard 101, six left digits, a centre guard 01010, six right digits and an end guard 101.

The first digit is not drawn; it selects which of the six left digits use the odd-parity L set and which use the even-parity G set. That is what the PARITY table holds. G codes are the right-hand R codes read backwards, and R codes are the bitwise inverse of L, so only the L table is stored and the others are derived. Each 1 becomes a bar; runs of ones are merged into one rectangle to keep the SVG small.

The component adds a quiet zone of nine modules on each side and prints the human-readable digits beneath using a monospace font.

Adapting it

  • Accept 12 digits and append the check digit automatically, as done here.
  • Scale with the module prop for different print sizes.
  • Add a second encoder (Code 39 is similarly small) behind a type prop.

Pitfalls

  • Reject non-numeric input before encoding.
  • Print at least 80 percent of nominal size or scanners may fail.

How to use it

  1. Save as components/Ean13.tsx (no dependencies, works on the server).
  2. <Ean13 code="590123412345" /> draws the code and appends the check digit.
  3. Pass moduleWidth={2} to enlarge it.
  4. If you pass 13 digits the last one is validated and a mismatch throws.

Licence: DEV3 Snippet Licence. Free to use, change and ship in personal and commercial projects. Attribution is appreciated, not required. Please do not redistribute snippets as a collection.

Related snippets