{"id":26,"date":"2026-05-20T16:30:30","date_gmt":"2026-05-20T08:30:30","guid":{"rendered":"https:\/\/formvalidation.io\/blog\/?p=26"},"modified":"2026-05-20T16:37:24","modified_gmt":"2026-05-20T08:37:24","slug":"ean-validator","status":"publish","type":"post","link":"https:\/\/formvalidation.io\/blog\/ean-validator\/","title":{"rendered":"EAN \/ UPC Barcode Validator"},"content":{"rendered":"\n<h2 class=\"wp-block-heading\"><\/h2>\n\n\n\n<!--\n  EAN \/ UPC Barcode Validator\n  ----------------------------\n  Self-contained tool. Drop the entire contents of this file into a WordPress\n  \"Custom HTML\" block. Runs entirely in the browser \u2014 no server, no database,\n  no external requests. Validates EAN-13, EAN-8, and UPC-A barcodes by\n  computing the check digit per the GS1 standard.\n-->\n\n<style>\n  .fv-tool {\n    font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, \"Helvetica Neue\", Arial, sans-serif;\n    max-width: 640px;\n    margin: 2rem auto;\n    padding: 2rem;\n    background: #ffffff;\n    border-radius: 16px;\n    box-shadow: 0 4px 24px rgba(0, 0, 0, 0.08);\n    color: #1a1a1a;\n    line-height: 1.5;\n    box-sizing: border-box;\n  }\n  .fv-tool *, .fv-tool *::before, .fv-tool *::after { box-sizing: border-box; }\n  .fv-tool__header { margin-bottom: 1.5rem; }\n  .fv-tool__title {\n    font-size: 1.5rem;\n    font-weight: 700;\n    margin: 0 0 0.5rem;\n    color: #111;\n    line-height: 1.25;\n  }\n  .fv-tool__subtitle {\n    font-size: 0.9375rem;\n    color: #555;\n    margin: 0;\n  }\n  .fv-tool__input-group {\n    display: flex;\n    gap: 0.5rem;\n    margin-bottom: 1rem;\n    flex-wrap: wrap;\n  }\n  .fv-tool__input {\n    flex: 1 1 240px;\n    font-family: \"SF Mono\", Menlo, Consolas, monospace;\n    font-size: 1rem;\n    padding: 0.75rem 1rem;\n    border: 2px solid #e0e0e0;\n    border-radius: 8px;\n    outline: none;\n    transition: border-color 0.15s, background 0.15s;\n    background: #fafafa;\n    color: #111;\n    letter-spacing: 0.025em;\n  }\n  .fv-tool__input:focus {\n    border-color: #2563eb;\n    background: #ffffff;\n  }\n  .fv-tool__btn {\n    font-family: inherit;\n    font-size: 1rem;\n    font-weight: 600;\n    padding: 0.75rem 1.5rem;\n    background: #2563eb;\n    color: #ffffff;\n    border: none;\n    border-radius: 8px;\n    cursor: pointer;\n    transition: background 0.15s, transform 0.05s;\n  }\n  .fv-tool__btn:hover { background: #1d4ed8; }\n  .fv-tool__btn:active { transform: translateY(1px); }\n  .fv-tool__result {\n    font-size: 0.9375rem;\n    padding: 0.875rem 1rem;\n    border-radius: 8px;\n    margin-bottom: 1rem;\n    display: none;\n    line-height: 1.5;\n  }\n  .fv-tool__result--valid {\n    display: block;\n    background: #ecfdf5;\n    color: #065f46;\n    border-left: 4px solid #10b981;\n  }\n  .fv-tool__result--invalid {\n    display: block;\n    background: #fef2f2;\n    color: #991b1b;\n    border-left: 4px solid #ef4444;\n  }\n  .fv-tool__result--empty {\n    display: block;\n    background: #f3f4f6;\n    color: #4b5563;\n    border-left: 4px solid #9ca3af;\n  }\n  .fv-tool__examples {\n    display: flex;\n    gap: 0.5rem;\n    flex-wrap: wrap;\n    align-items: center;\n    margin-bottom: 1.5rem;\n  }\n  .fv-tool__examples-label {\n    font-size: 0.875rem;\n    color: #6b7280;\n    margin-right: 0.25rem;\n  }\n  .fv-tool__chip {\n    font-family: inherit;\n    font-size: 0.8125rem;\n    font-weight: 500;\n    padding: 0.375rem 0.75rem;\n    background: #f3f4f6;\n    color: #374151;\n    border: 1px solid #e5e7eb;\n    border-radius: 999px;\n    cursor: pointer;\n    transition: background 0.15s, border-color 0.15s;\n  }\n  .fv-tool__chip:hover {\n    background: #e5e7eb;\n    border-color: #d1d5db;\n  }\n  .fv-tool__about {\n    font-size: 0.875rem;\n    color: #4b5563;\n    border-top: 1px solid #f0f0f0;\n    padding-top: 1rem;\n    margin-top: 0.5rem;\n  }\n  .fv-tool__about summary {\n    cursor: pointer;\n    font-weight: 600;\n    color: #2563eb;\n    user-select: none;\n    list-style: none;\n  }\n  .fv-tool__about summary::-webkit-details-marker { display: none; }\n  .fv-tool__about summary::before {\n    content: \"\u25b8 \";\n    display: inline-block;\n    transition: transform 0.15s;\n    margin-right: 0.25rem;\n  }\n  .fv-tool__about[open] summary::before { transform: rotate(90deg); }\n  .fv-tool__about summary:hover { color: #1d4ed8; }\n  .fv-tool__about p { margin: 0.75rem 0; }\n  @media (max-width: 480px) {\n    .fv-tool {\n      padding: 1.5rem;\n      margin: 1rem;\n      border-radius: 12px;\n    }\n    .fv-tool__title { font-size: 1.25rem; }\n    .fv-tool__input, .fv-tool__btn { width: 100%; flex: 1 1 100%; }\n  }\n<\/style>\n\n<div class=\"fv-tool\">\n  <div class=\"fv-tool__header\">\n    <h2 class=\"fv-tool__title\">EAN \/ UPC Barcode Validator<\/h2>\n    <p class=\"fv-tool__subtitle\">Check whether a barcode is valid (EAN-13, EAN-8, or UPC-A). The check runs in your browser \u2014 nothing is uploaded or stored.<\/p>\n  <\/div>\n\n  <div class=\"fv-tool__input-group\">\n    <input\n      type=\"text\"\n      id=\"fv-barcode-input\"\n      class=\"fv-tool__input\"\n      placeholder=\"Enter barcode, e.g. 5901234123457\"\n      inputmode=\"numeric\"\n      autocomplete=\"off\"\n      spellcheck=\"false\"\n    \/>\n    <button class=\"fv-tool__btn\" id=\"fv-check-btn\" type=\"button\">Check barcode<\/button>\n  <\/div>\n\n  <div class=\"fv-tool__result\" id=\"fv-result\" role=\"status\" aria-live=\"polite\"><\/div>\n\n  <div class=\"fv-tool__examples\">\n    <span class=\"fv-tool__examples-label\">Try an example:<\/span>\n    <button class=\"fv-tool__chip\" type=\"button\" data-code=\"5901234123457\">EAN-13<\/button>\n    <button class=\"fv-tool__chip\" type=\"button\" data-code=\"042100005264\">UPC-A<\/button>\n    <button class=\"fv-tool__chip\" type=\"button\" data-code=\"12345670\">EAN-8<\/button>\n    <button class=\"fv-tool__chip\" type=\"button\" data-code=\"5901234123450\">Invalid example<\/button>\n  <\/div>\n\n  <details class=\"fv-tool__about\">\n    <summary>How does this work?<\/summary>\n    <p>EAN-13, EAN-8, and UPC-A barcodes use a <em>check digit<\/em>: the last digit is mathematically derived from the preceding digits using a weighted sum (alternating weights of 3 and 1). If you mistype a digit, the check fails. This tool runs that calculation on whatever you enter.<\/p>\n    <p>EAN-13 has 13 digits (used worldwide on most retail products). UPC-A has 12 digits (common in North America). EAN-8 has 8 digits (used on small packages).<\/p>\n    <p>Note: a valid check digit confirms the barcode is well-formed, not that the product exists.<\/p>\n  <\/details>\n<\/div>\n\n<script>\n(function () {\n  function computeCheckDigit(digits) {\n    var sum = 0;\n    for (var i = digits.length - 1; i >= 0; i--) {\n      var weight = (digits.length - 1 - i) % 2 === 0 ? 3 : 1;\n      sum += digits[i] * weight;\n    }\n    return (10 - (sum % 10)) % 10;\n  }\n\n  function validate(raw) {\n    var clean = String(raw || '').replace(\/[\\s-]\/g, '');\n    if (!clean) {\n      return { state: 'empty', message: 'Enter a barcode to check.' };\n    }\n    if (!\/^\\d+$\/.test(clean)) {\n      return { state: 'invalid', message: 'Barcode must contain only digits (spaces and dashes are ignored).' };\n    }\n    var len = clean.length;\n    var format;\n    if (len === 8) format = 'EAN-8';\n    else if (len === 12) format = 'UPC-A';\n    else if (len === 13) format = 'EAN-13';\n    else {\n      return {\n        state: 'invalid',\n        message: 'Length ' + len + ' is not a valid barcode length. Expected 8 (EAN-8), 12 (UPC-A), or 13 (EAN-13).'\n      };\n    }\n    var digits = clean.split('').map(Number);\n    var givenCheck = digits.pop();\n    var expectedCheck = computeCheckDigit(digits);\n    if (givenCheck === expectedCheck) {\n      return { state: 'valid', message: 'Valid ' + format + ' barcode: ' + clean };\n    }\n    return {\n      state: 'invalid',\n      message: 'Invalid ' + format + ': the check digit should be ' + expectedCheck + ', but the barcode ends with ' + givenCheck + '.'\n    };\n  }\n\n  var input = document.getElementById('fv-barcode-input');\n  var btn = document.getElementById('fv-check-btn');\n  var result = document.getElementById('fv-result');\n\n  function show(res) {\n    result.className = 'fv-tool__result fv-tool__result--' + res.state;\n    result.textContent = res.message;\n  }\n\n  function run() {\n    show(validate(input.value));\n  }\n\n  btn.addEventListener('click', run);\n  input.addEventListener('keydown', function (e) {\n    if (e.key === 'Enter') run();\n  });\n\n  var chips = document.querySelectorAll('.fv-tool__chip');\n  for (var i = 0; i < chips.length; i++) {\n    chips[i].addEventListener('click', function () {\n      input.value = this.getAttribute('data-code');\n      run();\n    });\n  }\n})();\n<\/script>\n\n\n\n<p class=\"wp-block-paragraph\">Check whether a barcode is valid (EAN-13, EAN-8, or UPC-A). The check runs in your browser \u2014 nothing is uploaded or stored.Check barcode<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Try an example:EAN-13UPC-AEAN-8Invalid example How does this work?<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">EAN-13, EAN-8, and UPC-A barcodes use a <em>check digit<\/em>: the last digit is mathematically derived from the preceding digits using a weighted sum (alternating weights of 3 and 1). If you mistype a digit, the check fails. This tool runs that calculation on whatever you enter.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">EAN-13 has 13 digits (used worldwide on most retail products). UPC-A has 12 digits (common in North America). EAN-8 has 8 digits (used on small packages).<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Note: a valid check digit confirms the barcode is well-formed, not that the product exists.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>EAN \/ UPC Barcode Validator Check whether a barcode is valid (EAN-13, EAN-8, or UPC-A). The check runs in your browser \u2014 nothing is uploaded or stored. Check barcode Try an example: EAN-13 UPC-A EAN-8 Invalid example How does this work? EAN-13, EAN-8, and UPC-A barcodes use a check digit: the last digit is mathematically [&hellip;]<\/p>\n","protected":false},"author":2,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[1],"tags":[],"class_list":["post-26","post","type-post","status-publish","format-standard","hentry","category-uncategorized"],"_links":{"self":[{"href":"https:\/\/formvalidation.io\/blog\/wp-json\/wp\/v2\/posts\/26","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/formvalidation.io\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/formvalidation.io\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/formvalidation.io\/blog\/wp-json\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/formvalidation.io\/blog\/wp-json\/wp\/v2\/comments?post=26"}],"version-history":[{"count":3,"href":"https:\/\/formvalidation.io\/blog\/wp-json\/wp\/v2\/posts\/26\/revisions"}],"predecessor-version":[{"id":29,"href":"https:\/\/formvalidation.io\/blog\/wp-json\/wp\/v2\/posts\/26\/revisions\/29"}],"wp:attachment":[{"href":"https:\/\/formvalidation.io\/blog\/wp-json\/wp\/v2\/media?parent=26"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/formvalidation.io\/blog\/wp-json\/wp\/v2\/categories?post=26"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/formvalidation.io\/blog\/wp-json\/wp\/v2\/tags?post=26"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}