How to Scan GS1 Barcodes and Parse Application Identifiers (GS1 AIs) with JavaScript
GS1 is a not-for-profit, international organization developing and maintaining its own standards for barcodes. GS1 defines the rules to encode a barcode content so that it contains information about a product: its GTIN (Global Trade Item Number), the weight or dimensions of the item, its price, the lot/batch code, the date when it was fabricated and so on.
A GS1 barcode is a concatenation of data elements. Every single element starts with an application identifier (AI), a two to four digits number. The number is followed by the actual information.
A data element is delimited either by:
- the end of the whole barcode
- a specification that states that this information has a fixed count of characters or digits
- a special character named FNC1 which is a non-printable character whose byte is 29. It is usually for information with variable length
The GS1 family of barcodes contains various barcode formats. Databar barcodes are most often used to label fresh foods or coupons. Code 128 and ITF-14 are often used in a distribution environment. 2D barcodes like Data Matrix and QR Code are used in a wide range of industries, from retail and manufacturing to logistics and healthcare.
Here are some GS1 barcode examples:
-
GS1 DataBar Stacked

-
GS1 DataMatrix

-
GS1 Composite

In the next part, let’s build a web barcode reader in HTML5 to scan and parse GS1 barcodes with the JavaScript edition of Dynamsoft Barcode Reader used as the barcode reading engine.
New Project
Create a new TypeScript project with Vite.
npm create vite@latest gs1barcodereader -- --template vanilla-ts
Add the Dependency
Install Dynamsoft Barcode Reader:
npm install dynamsoft-barcode-reader-bundle@11.0.6000
Initialize Dynamsoft Barcode Reader
In main.ts, initialize a reader instance of Capture Vision Router to call Dynamsoft Barcode Reader. A license is needed to use Dynamsoft Barcode Reader. You can apply for one here.
import { BarcodeResultItem, CodeParser, CodeParserModule, CoreModule } from "dynamsoft-barcode-reader-bundle";
import { LicenseManager } from "dynamsoft-barcode-reader-bundle";
import { CameraView, CameraEnhancer } from "dynamsoft-barcode-reader-bundle";
import { CaptureVisionRouter } from "dynamsoft-barcode-reader-bundle";
LicenseManager.initLicense("DLS2eyJvcmdhbml6YXRpb25JRCI6IjIwMDAwMSJ9");
let cvRouter: CaptureVisionRouter;
CoreModule.loadWasm();
init();
async function init(){
cvRouter = await CaptureVisionRouter.createInstance();
}
Read Barcodes from an Image
-
Add a hidden file input and use a button to trigger it.
HTML:
<div> <button id="decodeImageBtn">Decode an image</button> <input type="file" id="imageFile" style="display:none;"/> </div>JS:
document.getElementById("decodeImageBtn")?.addEventListener("click",async function(){ if (cvRouter) { document.getElementById("imageFile")?.click(); }else{ alert("Please wait for the initialization of Dynamsoft Barcode Reader."); } }); -
Add the event listener for
changeof the file input. When a new image is selected, display it on the page and read barcodes from it.document.getElementById("imageFile")?.addEventListener("change",function(){ let fileInput = document.getElementById("imageFile") as HTMLInputElement; if (fileInput.files && fileInput.files.length>0) { let file = fileInput.files[0]; let fileReader = new FileReader(); fileReader.onload = function(e){ loadImage(e.target?.result as string); }; fileReader.onerror = function () { console.warn('oops, something went wrong.'); }; fileReader.readAsDataURL(file); } }); function loadImage(dataURL:string){ let img = document.getElementById("selectedImg") as HTMLImageElement; img.onload = async function(){ let result = await cvRouter.capture(img,"ReadBarcodes_SpeedFirst"); let barcodes = result.decodedBarcodesResult?.barcodeResultItems ?? []; console.log(barcodes); } img.src = dataURL; }
Parse GS1 Barcodes
Now that we get the GS1 barcode results, we can try to parse them.
Here, we are going to use Dynamsoft Code Parser.
-
Initialize the Code Parser.
CodeParserModule.loadSpec("GS1_AI"); let parser:CodeParser; async function init(){ parser = await CodeParser.createInstance(); } -
Parse the GS1 barcodes.
async function parseGS1Barcode(result:BarcodeResultItem){ let text = result.text; let parsedItem = await parser.parse(text); const data = JSON.parse(parsedItem.jsonString); const items:any[] = []; if (data.ResultInfo) { (data.ResultInfo as any[]).forEach(item => { let ai = item.FieldName || ""; let description = ""; let value = ""; const childFields = item.ChildFields?.[0] || []; (childFields as any[]).forEach(field => { if (field.FieldName.endsWith("AI")) { ai = field.RawValue || ai; description = field.Value || ""; } else if (field.FieldName.endsWith("Data")) { value = field.Value || ""; } console.log(field); }); items.push({dataTitle:description,data:value}); }); } return items; } -
List the parsed results in a table.
async function buildBarcodeTable(result:BarcodeResultItem){ const table = document.createElement("table"); const items:{key:string,value:any}[] = []; items.push({key:"Format",value:result.formatString}); items.push({key:"Text",value:result.text}); items.push({key:"Bytes",value:result.bytes}); try { let codeItems; codeItems = await parseGS1Barcode(result); for (let index = 0; index < codeItems.length; index++) { const item = codeItems[index]; if (typeof(item.data) === "object" && "getYear" in item.data) { items.push({key:item.dataTitle,value:item.data.toDateString()}); }else{ items.push({key:item.dataTitle,value:item.data}); } } } catch (error) { console.log(error); } const headRow = document.createElement("tr"); const keyTitleCell = document.createElement("th"); keyTitleCell.innerText = "Key"; keyTitleCell.style.minWidth = "30vw"; const valueTitleCell = document.createElement("th"); valueTitleCell.innerText = "Value"; headRow.appendChild(keyTitleCell); headRow.appendChild(valueTitleCell) table.appendChild(headRow); for (let index = 0; index < items.length; index++) { const item = items[index]; const dataRow = document.createElement("tr"); const keyCell = document.createElement("td"); keyCell.innerText = item.key; const valueCell = document.createElement("td"); valueCell.innerText = item.value; dataRow.appendChild(keyCell); dataRow.appendChild(valueCell); table.appendChild(dataRow); } return table; }

The following is the table for the above GS1 barcode.
| Key | Value |
|---|---|
| Format | GS1 Composite Code |
| Text | 14987047104034|172512001023011 |
| Bytes | 49,52,57,56,55,48,52,55,49,48,52,48,51,52,49,55,50,53,49,50,48,48,49,48,50,51,48,49,49 |
| GTIN | 14987047104034 |
| USE BY OR EXPIRY | Wed Dec 31 2025 |
| BATCH/LOT | 23011 |
Add Live Scanning with the Camera
Next, we can add the live scanning ability so that the web barcode reader can read GS1 barcodes with the camera.
-
Add elements for the camera.
HTML:
<div id="scanner" style="display:none"> <button id="closeBtn">Close</button> </div>CSS:
#closeBtn { position: fixed; right: 0; top: 0; z-index: 999; } #scanner { position: absolute; left: 0; top: 0; width: 100%; height: 100%; background-color: white; } -
Initialize an instance of Dynamsoft Camera Enhancer and bind it to the UI element we just created.
import { CameraView, CameraEnhancer } from "dynamsoft-barcode-reader-bundle"; let enhancer:CameraEnhancer; async function init(){ const cameraView = await CameraView.createInstance(); enhancer = await CameraEnhancer.createInstance(cameraView); document.querySelector("#scanner")!.append(cameraView.getUIElement()); } -
Start the camera when the
scanbutton is clicked and stop the camera when theclosebutton is clicked.document.getElementById("liveScanBtn")?.addEventListener("click",function(){ startScan(); }); document.getElementById("closeBtn")?.addEventListener("click",function(){ stopScan(); }); function startScan(){ if (!cvRouter || !enhancer) { alert("Please wait for the initialization of Dynamsoft Barcode Reader."); return; } enhancer.open(); document.getElementById("scanner")!.style.display = "block"; } function stopScan(){ enhancer.close(); document.getElementById("scanner")!.style.display = "none"; } -
When the camera is opened, start a processing loop to read barcodes from the camera. If a barcode is found, capture the camera frame and list the parsed results.
enhancer.on("played",function(){ startDecodingLoop(); //start the loop when the camera is opened }); function startDecodingLoop(){ stopDecodingLoop(); interval = setInterval(captureAndDecode,50); } function stopDecodingLoop(){ clearInterval(interval); decoding = false; } async function captureAndDecode(){ if (decoding === true) { return; } if (cvRouter && enhancer) { if (enhancer.isOpen() === false) { return; } decoding = true; let frame = enhancer.fetchImage(); let result = await vRouter.capture(frame,"ReadBarcodes_SpeedFirst"); let results = result.decodedBarcodesResult?.barcodeResultItems ?? []; if (results.length > 0) { let img = document.getElementById("selectedImg") as HTMLImageElement; img.onload = function(){}; img.src = frame.toCanvas().toDataURL(); listResults(results); stopScan(); } decoding = false; } }
All right, we’ve now finished the web GS1 barcode reader.
Source Code
Check out the source code of the demo to have a try:
https://github.com/tony-xlh/GS1-Barcode-Reader