{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "barcode",
  "title": "Barcode",
  "description": "A scannable one-dimensional barcode, drawn as SVG, for the symbol a handheld scanner or a till actually reads — Code 128 for anything of your own, EAN-13 for a retail product number. Reach for it wherever a physical thing has to be identified by pointing a scanner at it: a shipping or courier label, a parcel or pallet, a packing slip, a warehouse bin or shelf location, a stock count or cycle count sheet, an asset tag, IT inventory, a serial or batch number, a returns label, an event or cinema ticket, a wristband, a visitor or conference badge, a library book, a patient or specimen label, a laboratory sample, a rental item, a till or point-of-sale product code, a price label, a product carton, and any label stock going through a thermal printer. Common asks it answers: \"barcode react\", \"react barcode component\", \"generate barcode react\", \"shadcn barcode\", \"code 128 react\", \"code128 generator javascript\", \"ean 13 react\", \"ean-13 generator\", \"upc barcode react\", \"jsbarcode alternative\", \"react-barcode alternative\", \"bwip-js alternative\", \"barcode without dependencies\", \"barcode no npm package\", \"barcode svg react\", \"barcode canvas vs svg\", \"barcode server component\", \"barcode next.js app router\", \"barcode ssr\", \"print barcode labels react\", \"thermal printer barcode html\", \"barcode not scanning\", \"barcode won't scan\", \"barcode scans on some scanners\", \"quiet zone barcode\", \"barcode module width\", \"barcode x dimension\", \"narrow bar width\", \"barcode blurry when printed\", \"barcode too small to scan\", \"barcode dark mode\", \"inverted barcode not scanning\", \"barcode check digit\", \"ean 13 check digit calculation\", \"code 128 check digit\", \"code 128 code set a b c\", \"code 128 switching\", \"barcode accessibility\", \"barcode screen reader\", \"barcode aria-label\", \"barcode with text underneath\", \"human readable barcode text\", \"barcode 12 digits vs 13\", \"upc-a vs ean-13\", \"バーコード react\", \"バーコード 生成\", \"一次元バーコード コンポーネント\", \"JAN コード react\", \"EAN-13 チェックディジット\". Official shadcn/ui has nothing here and the measurement is not close: fetching every entry in its registry today — 65 listed, 62 fetchable, code-block and questionnaire and trace 404 on both style tracks — and searching all 223,287 bytes of it, the strings barcode, Code128, code128, EAN-13, EAN13, GS1, checksum, checkDigit, quietZone, crispEdges, shapeRendering, symbology and scanner are every one of them a zero hit. So this gets solved by adding a package, and that is the first thing worth knowing about this one: both encoders are in the file. Code 128 in all three of its code sets, EAN-13 with its parity-encoded first digit, no dependencies at all, not even an icon, in a registry whose other components have never needed more than lucide-react. Because a barcode encoder's bugs are invisible — a wrong table row renders, prints, and is either never read or read as a different string — correctness here is measured rather than asserted. Every symbol this ships was rasterised from the path the component actually draws and read back by Apple's Vision framework, an independent decoder sharing no code with it: 153 symbols on 2026-10-29, all 153 byte for byte, covering every printable ASCII character, digit runs of every length to 24, runs embedded in text at every length where the code-set decision is a close call, control characters, 60 random payloads, all ten EAN-13 first-digit parity rows, every digit in every one of the twelve positions, and five published retail numbers. The sweep was then run against deliberately broken copies to prove it has teeth: one Code 128 row swapped with its neighbour failed 9 of 153, one EAN-13 digit pattern failed 18, and one corrupted parity row failed exactly the 2 codes beginning with the digit whose row had been changed. The verified symbols are frozen in the test suite so a shifted table row cannot come back. Beyond the encoding, four things decide whether a symbol actually works. The first is the width of its narrowest bar, which is why nothing here scales the picture to fit a container: a scanner resolves the narrowest element or it resolves nothing, so a symbol laid out at two pixels a module and then fitted into a card 70% as wide is not a smaller barcode, it is a broken one that still looks perfect. Whole pixels only, too — at 1.5px a module every other bar lands on a half pixel and the renderer spreads it over two, which turns the width ratios the decoder measures into something between 1:2 and 1:3. The second is the quiet zone, which the format specifies as a count of modules rather than a distance, so it shrinks with the symbol: ten modules for Code 128 and eleven for EAN-13 are drawn as part of the SVG rather than left to whatever margin the page happens to give it. The third is colour, which is functional rather than decorative and therefore does not follow your theme: decoders assume dark bars on a light ground, so the symbol stays black on white in dark mode — which is what a parcel label and a boarding pass do — with barClassName and backgroundClassName there for a tested exception. The fourth is height, because a scanner sweeping at an angle crosses a short symbol's bars diagonally and measures the wrong widths. The symbology is a choice you have to make and is never inferred from the payload: thirteen digits are valid for both, and a component that guessed would turn a shipping reference into a retail product code the first time one happened to be thirteen digits long. For Code 128 the hard part is where to change code set, since Code C packs two digits into one symbol but costs a symbol to enter and another to leave — four digits inside text break even, six are the first that pay — and this solves it exactly by search rather than approximately by the spec's lookahead rules, so a digit run is never longer than it has to be and a single borrowed character is shifted rather than switched to. Non-ASCII is refused by name rather than encoded through the mechanism scanners disagree about, which is how the accented character pasted from a spreadsheet surfaces before the label print rather than after. For EAN-13, twelve digits in means the thirteenth is computed and thirteen means it is verified — a mismatch throws with the digit it expected, because silently repairing a check digit prints a symbol that scans perfectly as the wrong item, and the number it was printed for is a real product somewhere. Spaces and hyphens are accepted since the numbers arrive grouped the way they are printed. It renders on the server and ships no JavaScript: no hooks, no state, no effects, so there is no \"use client\" on it, with a small internal cache keeping a re-rendering parent from re-encoding. It carries role=\"img\" and a name, and unlike a QR code it reads the value out by default — what a 1D symbol holds is a short identifier somebody may have to say out loud or type, not a URL or a secret, and an EAN-13 is announced in its printed groups so it is heard as digits rather than as one twelve-figure number. The printed line below the bars is the standard layout for EAN-13 and not decoration: the first digit outside the symbol on the left, two groups of six under their own halves, the guard bars dropping between them, each group stretched to span its half so the digits stay under their own bars whatever mono face the project uses. A Code 128 payload containing control characters gets no printed line at all rather than one full of whatever glyphs the font has for them. encodeCode128, encodeEan13, ean13CheckDigit and barcodePath are exported for a symbol you want to rasterise on a server, send to a label printer, or draw yourself. Within pulld it sits beside qr-code, which is the two-dimensional answer to a different question — a QR is pointed at by a phone, a barcode is swept by a scanner — and beside copy-field and copy-button, which are what you put next to it for everyone keying the number in by hand.",
  "files": [
    {
      "path": "registry/ui/barcode.tsx",
      "content": "import * as React from \"react\"\n\n/**\n * The two one-dimensional symbologies here.\n *\n * `\"code128\"` holds any ASCII text and is what a warehouse, a ticket or a shipping label uses;\n * `\"ean13\"` holds exactly thirteen digits and is the barcode on retail packaging. They are not\n * interchangeable and nothing picks between them for you: the same thirteen digits are a valid\n * payload for both, and a Code 128 symbol carrying a retail number will not be accepted by a till.\n */\nexport type Symbology = \"code128\" | \"ean13\"\n\n/** A finished symbol: the bar pattern, and the parameters that produced it. */\nexport interface Barcode {\n  symbology: Symbology\n  /**\n   * The string the scanner will hand back. For `\"ean13\"` this is thirteen digits including the\n   * check digit, which is not necessarily what was passed in — twelve digits in means the\n   * thirteenth was computed here.\n   */\n  value: string\n  /** `modules[i]` is true where a bar is dark. One module is the narrowest bar the symbol uses. */\n  modules: boolean[]\n  /** Light modules required either side before a scanner can find the symbol. */\n  quietZone: number\n  /**\n   * Half-open module ranges whose bars are drawn longer than the rest.\n   *\n   * EAN-13's three guard patterns, which on a retail label drop below the data bars and frame the\n   * two groups of digits. Empty for Code 128, which has no such convention.\n   */\n  guards: readonly (readonly [number, number])[]\n  /** Code 128 only: the symbol values, start and check and stop included. */\n  symbols?: number[]\n  /** EAN-13 only: the thirteenth digit, computed or confirmed. */\n  checkDigit?: number\n}\n\n// --- Code 128 --------------------------------------------------------------------------------------\n//\n// Each of the 107 symbol values is six elements wide — bar, space, bar, space, bar, space — and the\n// widths always total 11 modules. The stop value is the exception, seven elements and 13 modules,\n// which is how a scanner knows the symbol ended rather than guessing from a quiet zone it may not\n// have been given.\n//\n// This table is the one thing in this file that cannot be derived, and a single wrong row here is\n// invisible in the output: the symbol renders, prints, and is read as a different character or\n// refused for a check digit that no longer adds up. Four structural facts hold for every row and are\n// asserted in the test suite, which is cheap and catches the transcription mistakes that matter —\n// every row totals 11 modules, every element is 1 to 4 modules, every row is distinct, and the three\n// bars of every row sum to an even number. That last one is the format's own self-checking property,\n// so a row that breaks it is not a row of Code 128 at all.\n\nconst CODE128_PATTERNS = [\n  \"212222\", \"222122\", \"222221\", \"121223\", \"121322\", \"131222\", \"122213\", \"122312\", \"132212\", \"221213\",\n  \"221312\", \"231212\", \"112232\", \"122132\", \"122231\", \"113222\", \"123122\", \"123221\", \"223211\", \"221132\",\n  \"221231\", \"213212\", \"223112\", \"312131\", \"311222\", \"321122\", \"321221\", \"312212\", \"322112\", \"322211\",\n  \"212123\", \"212321\", \"232121\", \"111323\", \"131123\", \"131321\", \"112313\", \"132113\", \"132311\", \"211313\",\n  \"231113\", \"231311\", \"112133\", \"112331\", \"132131\", \"113123\", \"113321\", \"133121\", \"313121\", \"211331\",\n  \"231131\", \"213113\", \"213311\", \"213131\", \"311123\", \"311321\", \"331121\", \"312113\", \"312311\", \"332111\",\n  \"314111\", \"221411\", \"431111\", \"111224\", \"111422\", \"121124\", \"121421\", \"141122\", \"141221\", \"112214\",\n  \"112412\", \"122114\", \"122411\", \"142112\", \"142211\", \"241211\", \"221114\", \"413111\", \"241112\", \"134111\",\n  \"111242\", \"121142\", \"121241\", \"114212\", \"124112\", \"124211\", \"411212\", \"421112\", \"421211\", \"212141\",\n  \"214121\", \"412121\", \"111143\", \"111341\", \"131141\", \"114113\", \"114311\", \"411113\", \"411311\", \"113141\",\n  \"114131\", \"311141\", \"411131\", \"211412\", \"211214\", \"211232\", \"2331112\",\n] as const\n\n/** Start values. Which one is used is a consequence of the packing chosen below, not a setting. */\nconst START_A = 103\nconst START_B = 104\nconst START_C = 105\nconst STOP = 106\n\n/** Switch to the named set for everything that follows. The value depends on the set you are in. */\nconst TO_C_FROM_AB = 99\nconst TO_B = 100 // from A, and from C\nconst TO_A = 101 // from B, and from C\n/** Borrow one character from the other set and come straight back. Same value in A and in B. */\nconst SHIFT = 98\n\n/** The three packings, in the order ties are broken. B first: it holds the printable ASCII range. */\nconst CODE_SETS = [\"B\", \"A\", \"C\"] as const\nexport type Code128Set = (typeof CODE_SETS)[number]\n\nconst isDigit = (code: number) => code >= 48 && code <= 57\n\n/**\n * The value that encodes one character in set A or B, or -1 if that set cannot hold it.\n *\n * A covers 0–95: the printable characters up to the backtick, plus the control characters, which it\n * reaches by mapping them above the printables rather than below. B covers 32–127, so it gains lower\n * case and loses the control characters. Every ASCII character is therefore in at least one of them,\n * and only `[\\]^_` and the digits and capitals are in both.\n */\nfunction valueInSet(code: number, set: \"A\" | \"B\"): number {\n  if (set === \"A\") {\n    if (code >= 32 && code <= 95) return code - 32\n    if (code >= 0 && code <= 31) return code + 64\n    return -1\n  }\n  if (code >= 32 && code <= 127) return code - 32\n  return -1\n}\n\n/**\n * Pack `text` into the fewest symbols, by exhaustive search rather than by rule of thumb.\n *\n * Where to switch sets is the whole problem of encoding Code 128, and it is not a local decision.\n * Code C takes two digits per symbol, so a run of digits halves — but entering it costs a symbol and\n * leaving it costs another, which means a four-digit run inside text is a wash and a two-digit run is\n * a loss. The spec's recommended procedure is a set of lookahead rules that gets this nearly right;\n * the search below gets it exactly right for the same amount of code, by asking what the cheapest\n * encoding of the rest of the string is from each state and memoising the answer.\n *\n * Shortest matters beyond tidiness. Every symbol is 11 modules, so two wasted symbols on a label\n * printed at the usual density is about three millimetres of extra bar — the difference between a\n * symbol that fits the space the label stock gives it and one that is quietly scaled down until its\n * narrowest bar lands under the scanner's resolution.\n */\nfunction packCode128(text: string): number[] {\n  const n = text.length\n  const codes: number[] = []\n  for (let i = 0; i < n; i++) {\n    const code = text.charCodeAt(i)\n    if (code > 127) {\n      throw new RangeError(\n        `Code 128 holds ASCII only; ${JSON.stringify(text[i])} at index ${i} is U+${code\n          .toString(16)\n          .toUpperCase()\n          .padStart(4, \"0\")}`,\n      )\n    }\n    codes.push(code)\n  }\n\n  // best[i][set] — symbols needed to encode from i to the end, having arrived in `set`. The payload\n  // only; the start, check and stop symbols are added by the caller and cost the same either way.\n  const best = new Map<string, number>()\n  const move = new Map<string, { emit: number[]; next: number; set: Code128Set }>()\n\n  const solve = (i: number, set: Code128Set): number => {\n    if (i >= n) return 0\n    const key = `${i}${set}`\n    const seen = best.get(key)\n    if (seen !== undefined) return seen\n    // Guard against the search re-entering the same state: a switch always consumes a character\n    // before the next decision, so no cycle can form, but memoising a sentinel first keeps a future\n    // edit from turning this into a hang rather than a wrong answer.\n    best.set(key, Number.POSITIVE_INFINITY)\n\n    let bestCost = Number.POSITIVE_INFINITY\n    let bestMove: { emit: number[]; next: number; set: Code128Set } | null = null\n    const consider = (cost: number, emit: number[], next: number, nextSet: Code128Set) => {\n      if (cost >= bestCost) return\n      bestCost = cost\n      bestMove = { emit, next, set: nextSet }\n    }\n\n    // Stay where we are.\n    if (set === \"C\") {\n      if (i + 1 < n && isDigit(codes[i]) && isDigit(codes[i + 1])) {\n        consider(1 + solve(i + 2, \"C\"), [Number(text.slice(i, i + 2))], i + 2, \"C\")\n      }\n    } else {\n      const own = valueInSet(codes[i], set)\n      if (own >= 0) consider(1 + solve(i + 1, set), [own], i + 1, set)\n      // Borrow one character from the other letter set. Two symbols rather than the two a switch\n      // costs plus the switch back, so this wins for exactly one odd character in a run.\n      const other = set === \"A\" ? \"B\" : \"A\"\n      const borrowed = valueInSet(codes[i], other)\n      if (own < 0 && borrowed >= 0) {\n        consider(2 + solve(i + 1, set), [SHIFT, borrowed], i + 1, set)\n      }\n    }\n\n    // Or switch, and consume straight away in the set we land in. Switching without consuming is\n    // never shorter, so it is not a move the search needs to represent.\n    for (const target of CODE_SETS) {\n      if (target === set) continue\n      const to = target === \"C\" ? TO_C_FROM_AB : target === \"A\" ? TO_A : TO_B\n      if (target === \"C\") {\n        if (i + 1 < n && isDigit(codes[i]) && isDigit(codes[i + 1])) {\n          consider(2 + solve(i + 2, \"C\"), [to, Number(text.slice(i, i + 2))], i + 2, \"C\")\n        }\n        continue\n      }\n      const value = valueInSet(codes[i], target)\n      if (value >= 0) consider(2 + solve(i + 1, target), [to, value], i + 1, target)\n    }\n\n    best.set(key, bestCost)\n    if (bestMove) move.set(key, bestMove)\n    return bestCost\n  }\n\n  let startSet: Code128Set = \"B\"\n  let startCost = Number.POSITIVE_INFINITY\n  for (const set of CODE_SETS) {\n    const cost = solve(0, set)\n    if (cost < startCost) {\n      startCost = cost\n      startSet = set\n    }\n  }\n  if (!Number.isFinite(startCost)) {\n    // Unreachable for ASCII input, which every character already passed. Here so that a future\n    // change to the sets fails loudly rather than emitting a symbol with characters missing.\n    throw new RangeError(\"Code 128 cannot encode this text\")\n  }\n\n  const symbols = [startSet === \"A\" ? START_A : startSet === \"C\" ? START_C : START_B]\n  let at = 0\n  let set = startSet\n  while (at < n) {\n    const step = move.get(`${at}${set}`)\n    if (!step) throw new RangeError(\"Code 128 cannot encode this text\")\n    symbols.push(...step.emit)\n    at = step.next\n    set = step.set\n  }\n  return symbols\n}\n\n/**\n * The check symbol: the start value plus each payload value times its position, modulo 103.\n *\n * Weighted by position, so it catches two characters swapped — which an unweighted sum would not,\n * and which is the mistake a smudged or partly-read symbol most often produces. Position counts from\n * one at the first payload symbol; the start value is added unweighted.\n */\nexport function code128CheckSymbol(symbols: readonly number[]): number {\n  let sum = symbols[0]\n  for (let i = 1; i < symbols.length; i++) sum += symbols[i] * i\n  return sum % 103\n}\n\nexport interface EncodeCode128Result extends Barcode {\n  symbology: \"code128\"\n  symbols: number[]\n}\n\n/**\n * Encode `text` as Code 128, choosing the packing that produces the shortest symbol.\n *\n * Throws on an empty string and on anything outside ASCII — including the accented characters and\n * curly quotes that arrive from a spreadsheet, which is the common way a label pipeline fails. There\n * is a mechanism in the format for the upper half of Latin-1 (FNC4) but scanners disagree about it\n * to the point that the output is not dependable, so this declines rather than printing a symbol\n * that reads differently on different hardware.\n */\nexport function encodeCode128(text: string): EncodeCode128Result {\n  if (!text) throw new RangeError(\"Code 128 needs at least one character\")\n  const payload = packCode128(text)\n  const symbols = [...payload, code128CheckSymbol(payload), STOP]\n\n  const modules: boolean[] = []\n  for (const symbol of symbols) {\n    const pattern = CODE128_PATTERNS[symbol]\n    for (let e = 0; e < pattern.length; e++) {\n      const width = Number(pattern[e])\n      // Elements alternate, and every pattern starts with a bar. The stop value has seven of them\n      // rather than six, which is why this reads the length of the pattern instead of assuming it.\n      for (let w = 0; w < width; w++) modules.push(e % 2 === 0)\n    }\n  }\n\n  return {\n    symbology: \"code128\",\n    value: text,\n    modules,\n    // Ten modules, which is the format's minimum and is specified as a multiple of the module rather\n    // than a distance: print the same symbol smaller and the required margin shrinks with it.\n    quietZone: 10,\n    guards: [],\n    symbols,\n  }\n}\n\n// --- EAN-13 ----------------------------------------------------------------------------------------\n//\n// Ninety-five modules, fixed: a guard, six digits, a centre guard, six more digits, a guard. Every\n// digit is seven modules wide, and that is the whole reason the symbology can carry thirteen digits\n// in twelve digits' worth of space — the thirteenth is not drawn anywhere. It is encoded in the\n// *parity* of the left six, each of which is printed from one of two alternative patterns, and the\n// pattern of choices across those six positions spells out the first digit. Read the left half\n// without reading its parity and you get a twelve-digit number from a thirteen-digit symbol, which\n// is why a home-grown reader of these usually works on half the products in a shop.\n//\n// Only the odd-parity left patterns are tabulated. The other two sets are derived, because they are\n// derivable and a derived table cannot drift out of step with the one it came from: the right-hand\n// pattern of a digit is the bitwise complement of its left-hand one, and the even-parity left\n// pattern is that complement reversed.\n\nconst EAN_LEFT_ODD = [\n  \"0001101\", \"0011001\", \"0010011\", \"0111101\", \"0100011\",\n  \"0110001\", \"0101111\", \"0111011\", \"0110111\", \"0001011\",\n] as const\n\nconst complement = (pattern: string) => [...pattern].map((bit) => (bit === \"1\" ? \"0\" : \"1\")).join(\"\")\n\n/** The right half of the symbol. Even parity, and it starts with a bar where the left half starts with a space. */\nconst EAN_RIGHT = EAN_LEFT_ODD.map(complement)\n\n/** The alternative left patterns. Even parity, same first-and-last-element rule as the odd ones. */\nconst EAN_LEFT_EVEN = EAN_RIGHT.map((pattern) => [...pattern].reverse().join(\"\"))\n\n/**\n * Which of the left six digits are drawn even-parity, per value of the first digit.\n *\n * `true` is even. A leading zero is six odd patterns, which is exactly what makes a UPC-A symbol and\n * an EAN-13 symbol beginning with zero the same picture — the twelve-digit American number and the\n * thirteen-digit international one are the same barcode, read by different conventions.\n */\nconst EAN_FIRST_DIGIT_PARITY = [\n  \"OOOOOO\", \"OOEOEE\", \"OOEEOE\", \"OOEEEO\", \"OEOOEE\",\n  \"OEEOOE\", \"OEEEOO\", \"OEOEOE\", \"OEOEEO\", \"OEEOEO\",\n] as const\n\nconst EAN_START_GUARD = \"101\"\nconst EAN_CENTRE_GUARD = \"01010\"\nconst EAN_END_GUARD = \"101\"\n\n/**\n * The thirteenth digit of an EAN-13, from the first twelve.\n *\n * Positions alternate weight one and three from the left. Weighting is what makes it catch a\n * transposition: with every digit weighted the same, swapping two of them leaves the sum untouched,\n * and two digits swapped is precisely what happens when a number is read off a box by hand.\n */\nexport function ean13CheckDigit(digits: string): number {\n  if (!/^\\d{12}$/.test(digits)) throw new RangeError(\"EAN-13's check digit is computed from exactly 12 digits\")\n  let sum = 0\n  for (let i = 0; i < 12; i++) sum += Number(digits[i]) * (i % 2 === 0 ? 1 : 3)\n  return (10 - (sum % 10)) % 10\n}\n\nexport interface EncodeEan13Result extends Barcode {\n  symbology: \"ean13\"\n  checkDigit: number\n}\n\n/**\n * Encode twelve or thirteen digits as EAN-13.\n *\n * Twelve digits in means the check digit is computed and appended. Thirteen means it is *verified*,\n * and a mismatch throws rather than being corrected: a number typed or pasted with one digit wrong\n * is a real product somewhere, and silently repairing the check digit would print a symbol that\n * scans perfectly as the wrong item. The thrown message carries the digit that was expected, which\n * is the one piece of information needed to tell a typo from a bad check digit in the source data.\n *\n * Spaces and hyphens are accepted and ignored, because the numbers arrive grouped the way they are\n * printed. Nothing else is: a leading `+` or a decimal point means the value went through something\n * that treated it as a number, and the digits can no longer be trusted.\n */\nexport function encodeEan13(input: string): EncodeEan13Result {\n  const digits = input.replace(/[\\s-]/g, \"\")\n  if (!/^\\d{12,13}$/.test(digits)) {\n    throw new RangeError(`EAN-13 needs 12 or 13 digits; got ${JSON.stringify(input)}`)\n  }\n  const body = digits.slice(0, 12)\n  const check = ean13CheckDigit(body)\n  if (digits.length === 13 && Number(digits[12]) !== check) {\n    throw new RangeError(`EAN-13 check digit for ${body} is ${check}, not ${digits[12]}`)\n  }\n  const value = body + check\n\n  const parity = EAN_FIRST_DIGIT_PARITY[Number(value[0])]\n  let bits = EAN_START_GUARD\n  for (let i = 0; i < 6; i++) {\n    const digit = Number(value[i + 1])\n    bits += parity[i] === \"E\" ? EAN_LEFT_EVEN[digit] : EAN_LEFT_ODD[digit]\n  }\n  bits += EAN_CENTRE_GUARD\n  for (let i = 0; i < 6; i++) bits += EAN_RIGHT[Number(value[i + 7])]\n  bits += EAN_END_GUARD\n\n  return {\n    symbology: \"ean13\",\n    value,\n    modules: [...bits].map((bit) => bit === \"1\"),\n    // Eleven, which is the wider of the two margins the standard asks for and is applied to both\n    // sides here. The left one has to be wider because the first digit is printed in it.\n    quietZone: 11,\n    guards: [\n      [0, EAN_START_GUARD.length],\n      [45, 50],\n      [92, 95],\n    ],\n    checkDigit: check,\n  }\n}\n\n// --- drawing ---------------------------------------------------------------------------------------\n\nexport interface EncodeBarcodeOptions {\n  symbology?: Symbology\n}\n\n/** Encode by symbology. Throws for a payload the symbology cannot carry; see the two encoders. */\nexport function encodeBarcode(value: string, { symbology = \"code128\" }: EncodeBarcodeOptions = {}): Barcode {\n  return symbology === \"ean13\" ? encodeEan13(value) : encodeCode128(value)\n}\n\nexport interface BarcodePathOptions {\n  /** Width of one module. Whole numbers only if the result is going to be printed; see the note on the component. */\n  moduleWidth: number\n  /** Height of the data bars. */\n  height: number\n  /** How much further the guard bars run, in user units. Zero for a symbology with no guards. */\n  guardDrop?: number\n  /**\n   * Light modules to offset the bars by. Defaults to what the symbology requires.\n   *\n   * It has to be the same number the surrounding canvas was sized with. Drawing the bars at the\n   * symbology's own margin while the canvas is measured from a wider or narrower one puts the\n   * symbol off centre, and at the narrow end puts its stop pattern outside the viewBox — where it\n   * is clipped away, leaving a symbol that looks complete and ends in the middle of a character.\n   */\n  quietZone?: number\n}\n\n/**\n * One SVG path for every bar in the symbol.\n *\n * Adjacent dark modules are merged into a single rectangle first. Drawn one module at a time they\n * would meet along edges that the renderer antialiases, and a hairline of background colour down the\n * middle of a wide bar is exactly the kind of artefact a cheap laser scanner reports as two bars.\n */\nexport function barcodePath(\n  barcode: Barcode,\n  { moduleWidth, height, guardDrop = 0, quietZone = barcode.quietZone }: BarcodePathOptions,\n): string {\n  const extended = (index: number) => barcode.guards.some(([from, to]) => index >= from && index < to)\n  const parts: string[] = []\n  let run = 0\n  while (run < barcode.modules.length) {\n    if (!barcode.modules[run]) {\n      run++\n      continue\n    }\n    // A run is broken where the bar's height changes as well as where it ends, so that a guard bar\n    // never merges with the data bar beside it.\n    const tall = extended(run)\n    let end = run + 1\n    while (end < barcode.modules.length && barcode.modules[end] && extended(end) === tall) end++\n    const x = (quietZone + run) * moduleWidth\n    const width = (end - run) * moduleWidth\n    const barHeight = tall ? height + guardDrop : height\n    parts.push(`M${x} 0h${width}v${barHeight}h-${width}z`)\n    run = end\n  }\n  return parts.join(\"\")\n}\n\n// --- the component ---------------------------------------------------------------------------------\n\n// Encoding is pure, so a result can be shared between renders without a hook — which is the point. A\n// component reaching for useMemo would need \"use client\", and this one has no state, no events and\n// no effects: left as a plain function it renders on the server and ships no JavaScript at all for\n// what is a static picture. Bounded, because the common case is a handful of symbols on a label sheet\n// and the worst case is a field someone is typing a product number into.\nconst CACHE_LIMIT = 32\nconst cache = new Map<string, Barcode>()\n\nfunction encodeCached(value: string, symbology: Symbology): Barcode {\n  const key = `${symbology}|${value}`\n  const hit = cache.get(key)\n  if (hit) return hit\n  const barcode = encodeBarcode(value, { symbology })\n  cache.set(key, barcode)\n  if (cache.size > CACHE_LIMIT) {\n    const oldest = cache.keys().next()\n    if (!oldest.done) cache.delete(oldest.value)\n  }\n  return barcode\n}\n\n/** The printed digits of an EAN-13, in the groups the standard prints them in. */\nconst groupEan13 = (value: string) => [value.slice(0, 1), value.slice(1, 7), value.slice(7)] as const\n\nconst isPrintableAscii = (text: string) => /^[\\x20-\\x7e]*$/.test(text)\n\nexport interface BarcodeProps\n  extends Omit<\n    React.ComponentPropsWithoutRef<\"svg\">,\n    // `width` and `height` are taken over below: a barcode's size is not a free choice of two\n    // numbers, it is a module width and a bar height, and the overall size follows from them.\n    \"children\" | \"role\" | \"width\" | \"height\"\n  > {\n  /** Code 128: any ASCII text. EAN-13: twelve or thirteen digits, grouping characters allowed. */\n  value: string\n  /** Defaults to `\"code128\"`. Never inferred from the payload — see the note below. */\n  symbology?: Symbology\n  /**\n   * Width of the narrowest bar, in pixels. Defaults to 2.\n   *\n   * The one number that decides whether the symbol can be read, and the reason this component does\n   * not scale itself to its container. A scanner resolves the narrowest element or it resolves\n   * nothing, so a symbol laid out at 2px per module and then fitted into a card 70% as wide is not a\n   * smaller barcode, it is a broken one — and it still looks perfect. Whole numbers only: at 1.5px a\n   * module every other bar lands on a half pixel and the renderer spreads it over two, which turns\n   * the width ratios the decoder measures into something between 1:2 and 1:3.\n   */\n  moduleWidth?: number\n  /**\n   * Height of the data bars, in pixels. Defaults to 60.\n   *\n   * Guard bars and the printed digits are added below this, so the element is taller than this\n   * number for an EAN-13 or whenever the digits are shown. Height is not free either: a scanner\n   * sweeping at an angle crosses a short symbol's bars diagonally and reads the wrong widths, which\n   * is why retail symbols are as tall as they are.\n   */\n  height?: number\n  /** Light modules either side. Defaults to what the symbology requires — 10 for Code 128, 11 for EAN-13. */\n  quietZone?: number\n  /**\n   * Print the value below the bars. Defaults to true.\n   *\n   * For EAN-13 this is the standard layout and not decoration: the first digit sits outside the\n   * symbol on the left, the other two groups of six sit under their own halves, and the guard bars\n   * drop between them. It is what lets a cashier key the number in when the symbol will not scan,\n   * which is the one moment it matters.\n   */\n  showText?: boolean\n  /**\n   * Print this instead of the value.\n   *\n   * For a payload that is not meant to be read as it is encoded — a Code 128 symbol carrying control\n   * characters, or a number a person should see grouped. Note that a line which disagrees with the\n   * bars is a real hazard on a label, so this is for formatting the same information, not for\n   * labelling the symbol with something else.\n   */\n  text?: string\n  /** What a screen reader announces. Defaults to `\"Barcode\"`. */\n  label?: string\n  /**\n   * Include the value in that announcement. Defaults to true.\n   *\n   * The opposite default to a QR code, deliberately. What a 1D symbol carries is almost always a\n   * short identifier somebody may need to say out loud or type — a product number, an asset tag, a\n   * ticket reference — rather than a URL or a secret, and an EAN-13 is announced in its printed\n   * groups so that it is read as digits rather than as one enormous number.\n   */\n  announceValue?: boolean\n  /** Overrides the bars' fill, e.g. `\"fill-foreground\"`. Read the note on colour first. */\n  barClassName?: string\n  /** Overrides the ground's fill. `\"fill-transparent\"` lets the surface behind show through. */\n  backgroundClassName?: string\n  /** Overrides the printed line, for a different face or size. */\n  textClassName?: string\n  /** Rendered instead when the value cannot be encoded. Defaults to nothing. */\n  fallback?: React.ReactNode\n}\n\n/**\n * A scannable one-dimensional barcode, drawn as SVG.\n *\n * ```tsx\n * <Barcode value=\"PKG-4417-A\" />\n *\n * <Barcode value=\"4006381333931\" symbology=\"ean13\" />\n *\n * <Barcode value=\"0012345678905\" symbology=\"ean13\" moduleWidth={3} height={80} />\n * ```\n *\n * Both encoders are in this file — Code 128 in all three of its code sets with the switching solved\n * exactly, EAN-13 with its parity-encoded first digit — so there are no dependencies at all, not\n * even an icon. `encodeCode128`, `encodeEan13` and `barcodePath` are exported for a symbol you want\n * to rasterise on a server, send to a label printer, or draw yourself.\n *\n * **The symbology is a choice you have to make, and it is never guessed from the payload.** Thirteen\n * digits are a valid Code 128 payload and a valid EAN-13 one, and a component that quietly switched\n * would turn a shipping reference into a retail product code the first time one happened to be\n * thirteen digits long. Code 128 for anything of your own — orders, assets, tickets, locations.\n * EAN-13 only for a number issued by GS1 for a product that will cross a till.\n *\n * **It renders at its natural size and nothing here will shrink it to fit.** A barcode is read by\n * measuring its narrowest bar, so fitting one into a container is the single most common way a\n * symbol that looks finished turns out to be unreadable. Give it room, or raise `moduleWidth` and\n * give it more — and when it must go on paper, lay it out in whole pixels and print at the printer's\n * own resolution rather than letting a browser rescale it.\n *\n * **The colours are functional, so they do not follow the theme.** Decoders assume dark bars on a\n * light ground, and the margin either side has to be light for the symbol to be found at all, so the\n * ground is drawn white and the bars black in both themes — which is what a parcel label and a\n * boarding pass do in dark mode, and reads as deliberate rather than broken. `barClassName` and\n * `backgroundClassName` are there for a symbol you have tested with the hardware your users have;\n * keep the contrast high and the bars the darker of the two.\n *\n * It carries `role=\"img\"` and a name, and reads the value out by default. But a barcode is a thing\n * another device points at, so for someone using a screen reader it is not a route to anything on\n * its own: put the number on the page as selectable text as well, which for an EAN-13 the printed\n * line already does.\n */\nexport function Barcode({\n  value,\n  symbology = \"code128\",\n  moduleWidth = 2,\n  height = 60,\n  quietZone,\n  showText = true,\n  text,\n  label = \"Barcode\",\n  announceValue = true,\n  barClassName,\n  backgroundClassName,\n  textClassName,\n  fallback = null,\n  className,\n  ...props\n}: BarcodeProps) {\n  let barcode: Barcode | null = null\n  if (value) {\n    try {\n      barcode = encodeCached(value, symbology)\n    } catch {\n      // An unencodable value renders the fallback rather than throwing, so one bad row in a label\n      // sheet does not take the page with it. Call the encoders directly to handle it yourself —\n      // which is what a print job should do, since a missing symbol is worth stopping for.\n      barcode = null\n    }\n  }\n  if (!barcode) return <>{fallback}</>\n\n  const margin = quietZone ?? barcode.quietZone\n  const modules = margin * 2 + barcode.modules.length\n  const width = modules * moduleWidth\n\n  // Guards drop five modules below the data bars, which is where the digits go: the drop is what\n  // separates the two groups of six and marks the halves of the symbol.\n  const guardDrop = barcode.guards.length ? 5 * moduleWidth : 0\n  const fontSize = 7 * moduleWidth\n  const textBand = showText ? 8 * moduleWidth : 0\n  const totalHeight = height + Math.max(guardDrop, textBand)\n  const baseline = height + 6.5 * moduleWidth\n\n  const printed = text ?? barcode.value\n  // A Code 128 payload with control characters in it has no honest printed form, so the line is\n  // dropped rather than filled with the replacement glyphs the font happens to have — unless a\n  // `text` was given, which is the way to say what it should read.\n  const showLine = showText && (text !== undefined || isPrintableAscii(printed))\n  const groups = barcode.symbology === \"ean13\" ? groupEan13(printed) : null\n\n  // Grouped the way it is printed, so it is heard as digits rather than as one twelve-figure\n  // number. A payload with control characters in it has no spoken form either, for the same reason\n  // it has no printed one, so there the name stands on its own rather than carrying characters a\n  // reader will voice as nothing at all.\n  const announced = barcode.symbology === \"ean13\" ? groupEan13(barcode.value).join(\" \") : barcode.value\n  const name = announceValue && isPrintableAscii(announced) ? `${label}: ${announced}` : label\n\n  return (\n    <svg\n      xmlns=\"http://www.w3.org/2000/svg\"\n      viewBox={`0 0 ${width} ${totalHeight}`}\n      width={width}\n      height={totalHeight}\n      // Integer coordinates on a grid of rectangles: without this the renderer antialiases every\n      // bar edge, and the grey half-pixel it leaves is read as part of the bar by some scanners and\n      // as part of the space by others — which is the same as getting the widths wrong.\n      shapeRendering=\"crispEdges\"\n      role=\"img\"\n      aria-label={name}\n      className={className}\n      {...props}\n    >\n      {/* Drawn rather than left to the page, because the quiet zone has to be light for the symbol\n          to be found at all — and a transparent one takes the colour of whatever is behind it. */}\n      <rect width={width} height={totalHeight} className={backgroundClassName ?? \"fill-white\"} />\n      <path\n        // `margin`, not the symbology's own quiet zone: the two have to agree, or the bars are\n        // drawn from one origin inside a canvas measured from another.\n        d={barcodePath(barcode, { moduleWidth, height, guardDrop, quietZone: margin })}\n        className={barClassName ?? \"fill-black\"}\n      />\n      {showLine &&\n        (groups ? (\n          <>\n            {/* The first digit is printed outside the symbol, in the left margin — which is part of\n                why that margin is the wider of the two. */}\n            <text\n              x={(margin - 1) * moduleWidth}\n              y={baseline}\n              textAnchor=\"end\"\n              fontSize={fontSize}\n              fontFamily=\"ui-monospace, SFMono-Regular, Menlo, monospace\"\n              className={textClassName ?? barClassName ?? \"fill-black\"}\n            >\n              {groups[0]}\n            </text>\n            {/* Each group is stretched to span its own half of the symbol. Forcing the span rather\n                than trusting a font's metrics is what keeps the digits under their own bars in a\n                project whose mono face is not the one this was written against. */}\n            <text\n              x={(margin + 4) * moduleWidth}\n              y={baseline}\n              textLength={40 * moduleWidth}\n              lengthAdjust=\"spacing\"\n              fontSize={fontSize}\n              fontFamily=\"ui-monospace, SFMono-Regular, Menlo, monospace\"\n              className={textClassName ?? barClassName ?? \"fill-black\"}\n            >\n              {groups[1]}\n            </text>\n            <text\n              x={(margin + 51) * moduleWidth}\n              y={baseline}\n              textLength={40 * moduleWidth}\n              lengthAdjust=\"spacing\"\n              fontSize={fontSize}\n              fontFamily=\"ui-monospace, SFMono-Regular, Menlo, monospace\"\n              className={textClassName ?? barClassName ?? \"fill-black\"}\n            >\n              {groups[2]}\n            </text>\n          </>\n        ) : (\n          <text\n            x={width / 2}\n            y={baseline}\n            textAnchor=\"middle\"\n            fontSize={fontSize}\n            fontFamily=\"ui-monospace, SFMono-Regular, Menlo, monospace\"\n            className={textClassName ?? barClassName ?? \"fill-black\"}\n          >\n            {printed}\n          </text>\n        ))}\n    </svg>\n  )\n}\n",
      "type": "registry:ui"
    }
  ],
  "type": "registry:ui",
  "docs": "Install any pulld component by name: add \"@pulld\": \"https://pulld.pages.dev/r/{name}.json\" to the registries block in components.json, then `npx shadcn add @pulld/<name>`. All 140 components: https://pulld.pages.dev/?utm_source=cli"
}
