Skip to content

Building a GS1 barcode scanner and parser in JavaScript

Kevin March 27, 2025 8 mins read
app store

In this tutorial, you’ll learn how to create a browser-based barcode scanner that can parse the data encoded in various GS1 barcode types. The result will be a single stand-alone HTML file you can run on your server.

Scanning and parsing a GS1 barcode and its application identifiers

To achieve this, we’ll use plain JavaScript and the Scanbot Web Barcode Scanner SDK. We’re going to follow these steps:

  1. Setting up the barcode scanner
  2. Optimizing the scanner for GS1 barcodes
  3. Implementing the GS1 parser

Let’s get started!

Step 1: Set up the barcode scanner

In your project folder, create an index.html with some boilerplate code.

<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="UTF-8" />
        <meta
            name="viewport"
            content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0"
        />
        <title>GS1 Barcode Parser</title>
    </head>
    <body>    
    </body>
</html>

To set up our barcode scanner, we’ll have to do the following:

  1. Create a button that calls up the scanning interface when clicked.
  2. Include a <p> element on the page for displaying the scanning result.
  3. Import the Scanbot Web SDK using a CDN.
  4. Process the scan result before displaying it on the page.

⚠️ In this tutorial, we’re importing the SDK via jsDelivr. However, you should only do this for quick prototyping. In your production environment, please download the Web SDK directly (or install it via npm) and include its files in your project.

Your index.html will look something like this:

<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="UTF-8" />
        <meta
            name="viewport"
            content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0"
        />
        <title>GS1 Barcode Parser</title>
    </head>

    <body style="margin: 0">
        <button id="start-scanning">Start scanning</button>
        <p id="result"></p>
        <script type="module">
            import "https://cdn.jsdelivr.net/npm/scanbot-web-sdk@7.0.0/bundle/ScanbotSDK.ui2.min.js";
            const sdk = await ScanbotSDK.initialize({
                enginePath:
                    "https://cdn.jsdelivr.net/npm/scanbot-web-sdk@7.0.0/bundle/bin/barcode-scanner/",
            });
            document
                .getElementById("start-scanning")
                .addEventListener("click", async () => {

                    const config =
                        new ScanbotSDK.UI.Config.BarcodeScannerScreenConfiguration();

                    const scanResult = await ScanbotSDK.UI.createBarcodeScanner(config);
                    if (scanResult?.items?.length > 0) {
                        document.getElementById("result").innerText = scanResult.items[0].barcode.text;
                    } else {
                        document.getElementById("result").innerText = "Scanning aborted by the user";
                    }
                });
        </script>
    </body>
</html>

💡 We use Web Barcode Scanner SDK version 7.0.0 in this tutorial. You can find the latest version in the changelog.

This already provides us with a fully functional barcode scanner – although we still have to adjust it a bit to make it better suited for GS1 barcodes. Let’s do that now.

Step 2: Optimize the scanner for GS1 barcodes

As it is now, our scanner will read any barcode type. Restricting it to scanning only GS1-standardized symbologies prevents unintended scans and improves the scanner’s performance, as it doesn’t need to check each supported symbology.

There are GS1 standards for the following symbologies:

  • UPC
  • EAN
  • Code 128
  • ITF
  • DataBar
  • Data Matrix
  • QR Code

So let’s restrict scanning to these barcode types by modifying the BarcodeScannerScreenConfiguration we assigned to the config constant in the previous step.

config.scannerConfiguration.barcodeFormats = ["UPC_A", "UPC_E", "EAN_8", "EAN_13", "CODE_128", "ITF", "DATABAR", "DATABAR_EXPANDED", "DATABAR_LIMITED", "DATA_MATRIX", "QR_CODE"]

Right now, scanning a GS1 barcode with our app would output the encoded data without the special characters signaling the so-called application identifiers specified in the GS1 standard. We can change this by setting the scanner configuration’s gs1Handling property to "DECODE_FULL" (more about that in the SDK’s API documentation).

config.scannerConfiguration.gs1Handling = ScanbotSDK.Config.Gs1HandlingValues[4]

If you want, you can also change the viewfinder’s aspect ratio. It’s square by default, but since many GS1 barcodes come in a horizontal format, it makes sense to also convey this in our scanning interface. This is mainly a cosmetic change, but do keep in mind that only barcodes located inside the viewfinder will be scanned.

config.viewFinder.aspectRatio.height = 1;
config.viewFinder.aspectRatio.width = 2;

The result looks like this:

<!-- Existing code -->
document
    .getElementById("start-scanning")
    .addEventListener("click", async () => {
        const config =
            new ScanbotSDK.UI.Config.BarcodeScannerScreenConfiguration();
        config.scannerConfiguration.barcodeFormats = ["UPC_A", "UPC_E", "EAN_8", "EAN_13", "CODE_128", "ITF", "DATABAR", "DATABAR_EXPANDED", "DATABAR_LIMITED", "DATA_MATRIX", "QR_CODE"]
        config.viewFinder.aspectRatio.height = 1;
        config.viewFinder.aspectRatio.width = 2;

        const scanResult = await ScanbotSDK.UI.createBarcodeScanner(config);

        if (scanResult?.items?.length > 0) {
            document.getElementById("result").innerText = scanResult.items[0].barcode.text;
        } else {
            document.getElementById("result").innerText = "Scanning aborted by the user";
        }
    });

Now run the app to test its functionalities.

Scanning a GS1 barcode and outputting its value without parsing

💡 To test your web app on your phone, you have a few options. One option is to use a service like ngrok, which creates a tunnel to one of their SSL-certified domains. Their Quick Start guide will help you get up and running quickly.

If you’re planning to use the barcode scanner with a backend that can parse GS1 barcodes, the app as it is now should be enough. But if you need to parse the encoded information beforehand, you can take the scanResult object and process it further.

Step 3: Implement the GS1 parser

Let’s take a look at the following GS1-128 barcode:

A GS1-128 barcode with two application identifiers

Since the encoded sequence of numbers is also shown underneath in a human-readable form, you can easily recognize the two application identifiers in brackets.

“(01)” is the application identifier for the Global Trade Item Number (GTIN), followed by the actual GTIN: 09521234543213. “(3103)” is the application identifier for our fictional product’s net weight in kilograms – in this case 123 kg.

To retrieve this information automatically from a GS1 barcode, we need to configure our app to look for application identifiers and the corresponding values in the returned scanResult object. We can do that with the following code:

if (scanResult?.items?.length > 0) {
    if (scanResult.items[0].barcode.isGS1Message) {
        const data = scanResult.items[0].barcode.extractedDocument;
        if (!data?.children?.length) {
            document.getElementById("result").innerText = "";
            return;
        }

        const results = data.children
            .map((child) => {
                const fields = child.fields;
                if (!fields?.length) return undefined;

                const dataSet = { label: "", value: "" };

                for (const field of fields) {
                    const typeName = field.type?.name;
                    const text = field.value?.text || "";

                    if (typeName === "ElementDescription") {
                        dataSet.label = text;
                    } else if (typeName === "RawValue") {
                        dataSet.value = text;
                    }
                }

                return dataSet;
            })
            .filter((field) => field !== undefined);

        if (!results.length) {
            document.getElementById("result").innerText = "";
            return;
        }

        document.getElementById("result").innerText = results
            .map((item) => `${item.label}: ${item.value}\n`)
            .join("\n");
    } else {
        document.getElementById("result").innerText = "No application identifiers found";
    }
} else {
    document.getElementById("result").innerText = "Scanning aborted by the user";
}

With these changes, our final index.html looks like this:

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0" />
    <title>GS1 Barcode Parser</title>
</head>

<body style="margin: 0">
    <button id="start-scanning">Start scanning</button>
    <p id="result"></p>
    <script type="module">
        import "https://cdn.jsdelivr.net/npm/scanbot-web-sdk@7.0.0/bundle/ScanbotSDK.ui2.min.js";
        const sdk = await ScanbotSDK.initialize({
            enginePath:
                "https://cdn.jsdelivr.net/npm/scanbot-web-sdk@7.0.0/bundle/bin/barcode-scanner/",
        });
        document
            .getElementById("start-scanning")
            .addEventListener("click", async () => {
                const config =
                    new ScanbotSDK.UI.Config.BarcodeScannerScreenConfiguration();

                config.scannerConfiguration.barcodeFormats =
                    ["UPC_A", "UPC_E", "EAN_8", "EAN_13", "CODE_128", "ITF", "DATABAR", "DATABAR_EXPANDED", "DATABAR_LIMITED", "DATA_MATRIX", "QR_CODE"]

                config.scannerConfiguration.gs1Handling = ScanbotSDK.Config.Gs1HandlingValues[4]

                config.viewFinder.aspectRatio.height = 1;
                config.viewFinder.aspectRatio.width = 2;

                const scanResult = await ScanbotSDK.UI.createBarcodeScanner(config);

                if (scanResult?.items?.length > 0) {
                    if (scanResult.items[0].barcode.isGS1Message) {
                        const data = scanResult.items[0].barcode.extractedDocument;
                        if (!data?.children?.length) {
                            document.getElementById("result").innerText = "";
                            return;
                        }

                        const results = data.children
                            .map((child) => {
                                const fields = child.fields;
                                if (!fields?.length) return undefined;

                                const dataSet = { label: "", value: "" };

                                for (const field of fields) {
                                    const typeName = field.type?.name;
                                    const text = field.value?.text || "";

                                    if (typeName === "ElementDescription") {
                                        dataSet.label = text;
                                    } else if (typeName === "RawValue") {
                                        dataSet.value = text;
                                    }
                                }

                                return dataSet;
                            })
                            .filter((field) => field !== undefined);

                        if (!results.length) {
                            document.getElementById("result").innerText = "";
                            return;
                        }

                        document.getElementById("result").innerText = results
                            .map((item) => `${item.label}: ${item.value}\n`)
                            .join("\n");
                    } else {
                        document.getElementById("result").innerText = "No application identifiers found";
                    }
                } else {
                    document.getElementById("result").innerText = "Scanning aborted by the user";
                }
            });
    </script>
</body>

</html>

Now run the app again and scan a GS1 barcode with application identifiers …

Various GS1 barcodes with application identifiers

… to test your scanner!

Scanning and parsing a GS1 barcode and its application identifiers

Conclusion

This is just one of the many scanner configurations the Scanbot SDK has to offer – take a look at the RTU UI documentation and API reference to learn more.

Should you have questions about this tutorial or run into any issues, we’re happy to help! Just shoot us an email via tutorial-support@scanbot.io.

Happy scanning! 🤳