# Getting Started
A step-by-step guide on how to integrate the STRICH SDK into your app and get started with barcode scanning.
Tip:
This guide covers integrating STRICH into an existing web app, and assumes you are familiar with web development
topics like HTML, JavaScript, CSS and HTTP.
## Installation
STRICH can be installed in multiple ways, depending on your environment or workflow.
### Installing via NPM and a bundler
If you're using a bundler like [Vite](https://vite.dev/) or [esbuild](https://esbuild.github.io/),
you can simply install STRICH via [NPM](https://www.npmjs.com/package/@pixelverse/strichjs-sdk).
```SHELL
npm install @pixelverse/strichjs-sdk
```
STRICH uses [semantic versioning](https://semver.org/). By default, npm will add a version
requirement of `^x.y.z`, which will automatically update the dependency to the
latest minor version every time you run npm install.
Note:
If barcode scanning is a critical function in your app, we
recommend a more conservative approach: either use `~x.y.z` which will only
automatically apply patches or `x.y.z` to pin an exact version.
### Loading from a CDN
Alternatively, you can choose to load STRICH from the
[jsDeliver](https://www.jsdelivr.com/package/npm/@pixelverse/strichjs-sdk) CDN.
Tip:
While obtaining the library from a CDN is convenient, for PWAs we recommend bundling it with your app
so your app is self-contained and offline-capable.
#### ES6 Modules
Use an [import](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/import)
statement to load STRICH as an
[ES6 module](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Modules).
```JAVASCRIPT
import {StrichSDK, BarcodeReader} from "https://cdn.jsdelivr.net/npm/@pixelverse/strichjs-sdk@latest";
```
The same recommendations apply regarding versioning. To pin a version, change the URL to include the version:
```JAVASCRIPT
import {StrichSDK, BarcodeReader} from "https://cdn.jsdelivr.net/npm/@pixelverse/strichjs-sdk@1.11.0";
```
####
Global object (
```
## Creating a license key
To initialize the SDK, you need to obtain a license key first. License keys can be created in the
[Customer Portal](https://portal.strich.io) after creating an account and starting the free 30-day trial.
License keys are restricted to one or more application URLs (the scope of the license). So if your
scanning app will be available at https://app.example.com and you have
staging and development environments, you need to add those URLs to the license key when creating it.
You can also create separate license keys if
you already have a mechanism for injecting per-environment configurations.

Please note that HTTPS is required. Web browsers only allow access to the camera feed for secure origins. For more
information on how to serve from a secure origin during development, please refer to the
[Deployment Guide](deployment-guide.html).
Tip:
Loopback and private IP address ranges such as localhost/127.0.0.1 and 192.168.x.y which are often used during
development need not be specified. These are automatically included in the license key.
## Initializing the SDK
The SDK needs to be initialized once with the license key created previously by calling
[StrichSDK.initialize()](https://docs.strich.io/reference/classes/StrichSDK.html#initialize).
When the Promise resolves successfully, the SDK is ready to use.
```JAVASCRIPT
StrichSDK.initialize('eyJhbGciOiJIUzI1NiIsInR...')
.then(() => console.log('STRICH SDK initialized'));
```
If initialization fails, the Promise will reject with an [SdkError](https://docs.strich.io/reference/classes/SdkError.html).
The [message](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error/message)
and [detailMessage](https://docs.strich.io/reference/classes/SdkError.html#detailMessage) fields
should provide an enough information to diagnose the issue. A
[list of common causes and their remedies](https://kb.strich.io/article/9-sdk-initialization-errors)
is available in our Knowledge Base.
In Single Page Apps (SPAs), we recommend doing the SDK initialization early, before the user expects
to see the barcode scanning screen, for instance during the rest of your app initialization. SDK
initialization is asynchronous and may require internet connectivity (successful
license checks persist up to 48 hours), except for Enterprise licenses
which support offline operation as an add-on.
## Choosing the Integration
As of version 1.6.0 of the SDK, we now support two kinds of integrations:
* BarcodeReader: An embeddable barcode reader that you control. You supply a container in your app UI and are in charge of managing the BarcodeReader. Allows full flexibility in designing your scanning experience.
* PopupScanner: An out-of-the-box scanning experience presented in a modal dialog. Perfect for simple scanning use cases that do not require a lot of customization. No changes to your app UI are necessary. Scan barcodes with a single line of code.

The remainder of this guide will focus on the BarcodeReader integration. Please refer
to the [PopupScanner](the-popup-scanner.html) documentation if you want to explore that integration,
or if you want to quickly verify that the SDK is working.
## Integrating the BarcodeReader
### Defining the Host Element
We need to provide the BarcodeReader with a place to live. We do so by defining a
host element – a DOM element that will contain the UI, i.e. the camera feed, view finder, camera controls, etc.
#### Example: Vertical Layout with BarcodeReader on Top
A typical scanning app has a vertical layout with the scanner on top, results below and an action bar at the bottom.
```HTML
```
Here, the host element is a div with ID `scanner`, so we can refer to it via the CSS selector `#scanner`.
Tip:
The host element must use `relative` positioning and should not change its size during operation.
The selector should only refer to a single element.
### Creating a Configuration
Before we can create the BarcodeReader, we need to create a configuration – an object that specifies
the functional and visual characteristics of the BarcodeReader.
For a good scanning experience, it is important to restrict the [types of barcodes](https://docs.strich.io/supported-symbologies.html) to be detected by the BarcodeReader
to only the ones you need. STRICH will automatically set the region of interest (the area where barcodes are detected)
to something appropriate for selected barcode types.
Tip:
Fewer types of barcodes being searched in a smaller area lead to faster processing = quicker detection times.
#### Example: Configuration for Code 128 Barcodes
In the example below, we only look for [Code 128](https://docs.strich.io/code-128.html)
barcodes. We also set a [duplicateInterval](https://docs.strich.io/reference/interfaces/EngineConfiguration.html#duplicateInterval) of 2.5 seconds, avoiding repeated scans of the same code over a short
time frame.
```JAVASCRIPT
let config = {
// the CSS selector identifying the host element
selector: '#scanner',
engine: {
// restrict to the required symbologies
symbologies: ['code128'],
// filter duplicate results
duplicateInterval: 2500
}
};
```
### Initializing the BarcodeReader
Initializing the BarcodeReader is a two-step process: the configuration is passed to
the [constructor](https://docs.strich.io/reference/classes/BarcodeReader.html#constructor),
then the [initialize()](https://docs.strich.io/reference/classes/BarcodeReader.html#initialize) method is called, which will request access to the camera.
Barcode recognition starts once you call the [start()](https://docs.strich.io/reference/classes/BarcodeReader.html#start) method.
To receive barcode detections in your app code, provide a callback using the
[detected](https://docs.strich.io/reference/classes/BarcodeReader.html#detected) property.
The BarcodeReader will invoke this callback whenever barcodes are detected.
#### Example: Initializing and Starting a BarcodeReader
```JAVASCRIPT
new BarcodeReader(config).initialize()
.then(result => {
// initialization successful, store BarcodeReader for later
this.barcodeReader = result;
// register handler for code detections
this.barcodeReader.detected = (detections) => {
// .. do something with the scanned codes
};
// start reading barcodes!
return this.barcodeReader.start();
})
.catch(error => {
// initialization failed (error message displayed in BarcodeReader)
});
```
#### Camera Access
Initialization requires camera access, which is gated through a browser prompt.
It is usually a good idea to make the user aware of this prior to initializing the BarcodeReader, for example by displaying a
camera access will be required in the next step notice.
The SDK provides utility functions for determining
the [current camera permission state](https://docs.strich.io/reference/classes/StrichSDK.html#getCameraPermissionState)
and whether the device actually [has a camera](https://docs.strich.io/reference/classes/StrichSDK.html#hasCameraDevice).
### Destroying the BarcodeReader
When you are done with BarcodeReader, it needs to be released by calling the
[destroy](https://docs.strich.io/reference/classes/BarcodeReader.html#destroy) method.
Tip:
Destroying the BarcodeReader is important as it releases the camera. Failure to do so will lead
to camera access errors in subsequent initializations.
## Testing on a Smartphone
STRICH is optimized for barcode scanning in web apps running on smartphones.
It is very important to always test your app on actual smartphones.
During development, you are probably running your app locally on your desktop or laptop.
When accessing your app from a smartphone on the same network, you might have
to do some extra work to set up SSL, as camera access only works if the app is hosted
via HTTPS or is accessed via localhost, a [secure context](https://developer.mozilla.org/en-US/docs/Web/Security/Secure_Contexts).
Please refer to the [Deployment Guide](https://docs.strich.io/deployment-guide.html#serving-from-a-secure-origin)
for more information about how to expose your app via HTTPS during development.
Tip:
Tools like [ngrok](https://ngrok.com/use-cases#development) or
[DevTunnels](https://learn.microsoft.com/en-us/azure/developer/dev-tunnels/overview) make developing scanning apps much simpler, as they provide a publicly reachable
endpoint with automatic SSL. This is very useful for testing a locally hosted app on a smartphone.
## Next Steps
You are now ready to scan barcodes in your web app.
For further advice, check out these additional resources.
* [API Reference](https://docs.strich.io/reference/index.html) – explore the SDK and its features with our reference documentation.
* [Deployment Guide](deployment-guide.html) – What to know when taking your app live.
* [Best Practices for Barcode Scanning Apps](best-practices-for-scanning-apps.html) – Observing these practices ensures a good scanning experience for your users.
* [Sample code repositories on Github](sample-code.html)
# The Popup Scanner
## Introduction
[PopupScanner](https://docs.strich.io/reference/classes/PopupScanner.html)
is a pre-built modal scanning dialog that you can integrate into your app with just a single line of code.
The entire UI and management of the underlying [BarcodeReader](https://docs.strich.io/reference/classes/BarcodeReader.html) is taken care of for you.
It is intended for use cases where tight integration with the host app UX and custom styling is less important.
Example: Scanning a serial number barcode using PopupScanner
[Video](images/popup_scanner_demo.mp4)
## Scanning Barcodes
### Scanning Single Barcodes
To scan a barcode, call the
[PopupScanner.scan()](https://docs.strich.io/reference/classes/PopupScanner.html#scan) method and
supply a [PopupConfiguration](https://docs.strich.io/reference/interfaces/PopupConfiguration.html) as the argument.
The SDK needs to be in an [initialized](getting-started.html#initializing-the-sdk) state.
Scanning is an asynchronous operation that returns a
[Promise](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise),
and should be awaited using the [await](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/await) operator.
The method returns as soon as a matching barcode is read, and returns `undefined` in case the user dismissed the dialog
without scanning a barcode.
Example: Scanning Code 128 barcodes using PopupScanner
```JAVASCRIPT
// invoke PopupScanner, restrict to Code 128 symbology
let barcodes = await PopupScanner.scan({ symbologies: ['code128'] });
if (barcodes) {
// process the scanned barcode value
handleBarcodeScan(barcodes[0].data);
} else {
// handle user cancellation...
}
```
### Scanning Multiple Barcodes
Scanning a sequence of barcodes within the same popup can be achieved by setting
a [detectionHandler](https://docs.strich.io/reference/interfaces/PopupConfiguration.html#detectionHandler) in
the [Configuration](https://docs.strich.io/reference/interfaces/PopupConfiguration.html) passed
to [PopupScanner.scan()](https://docs.strich.io/reference/classes/PopupScanner.html#scan).
The detection handler is a piece of code that indicates to the PopupScanner if
scanning should continue or finish after a barcode was detected.
Our [JavaScript sample](https://github.com/pixelverse-llc/strich-javascript-sample/blob/57b12f40f419016763d4e77328a052db53d9a172/scan-popup.html#L129)
on Github illustrates how a detection handler can be used to differentiate between different three
different barcodes of the same symbology using regular expressions, and stop scanning once all three have been scanned.
## Popup Scanner Elements
The Popup Scanner is a semi-translucent dialog that pops up over your app’s UI. There's a small margin
on all sides so it is clear that the popup is shown in the context of your app.

### Title Area
The title area is used for user guidance — we recommend using something specific to the use case as the text, like Scan serial number or
Scan document ID. The default text is Scan Barcode and is localized based on the browser’s locale.
For further customization, a background color matching your app’s color palette can be set.
### Active Area
The active area or region of interest is bordered by a rectangle. Its dimensions are set automatically
based on the configured 1D and 2D symbologies.
### Cancel Button
The cancel button dismisses the dialog, without scanning a barcode. This causes the
[PopupScanner.scan()](https://docs.strich.io/reference/classes/PopupScanner.html#scan) to return `undefined`.
The default text is Cancel and is localized based on the browser’s locale.
The text and the button background color can be overridden from their defaults.
## Sample Configuration
The following snippet illustrates the full extent of customization that can be achieved through
[PopupConfiguration](https://docs.strich.io/reference/interfaces/PopupConfiguration.html).
```JAVASCRIPT
// A fully customized popup scanner configuration, for illustration
const popupCfg = {
/**
* The symbologies to recognize. Here we use Code 128 with a
* length of 15...17 characters and QR codes */
*/
symbologies: [{ name:'code128', 'minLen':15, 'maxLen':17}, 'qr'],
/**
* Overrides for textual elements
*/
labels: {
title: 'Scan IMEI Number',
cancel: 'Abort Scanning'
},
/**
* Overrides for background colors.
*/
style: {
cancelButtonBackgroundColor: 'rgba(1,0,0,0.5)',
cancelButtonColor: 'rgb(1,1,1)',
titleBackgroundColor: 'rgb(0,0,0)',
titleColor: 'rgb(1,1,1)',
},
/**
* Choose full-hd for dense 2D or long 1D codes, otherwise hd
* is fine.
*/
resolution: 'hd',
/**
* Configure audible and haptic feedback.
*/
feedback: {
audio: true,
vibration: true
},
/**
* Custom code to further validate barcodes.
* Here we check against a regular expression.
*/
detectionHandler: (detections) => {
return detections.some(d => d.data.match('\d{15,17}'));
}
}
```
## Sample Code
A vanilla JavaScript sample is available on [Github](https://github.com/pixelverse-llc/strich-javascript-sample/blob/main/scan-popup.html)
illustrating how to use PopupScanner for filling form fields.
It also demonstrates a more complex [detectionHandler](https://docs.strich.io/reference/interfaces/PopupConfiguration.html#detectionHandler) that supports scanning multiple codes in succession.
## Requirements
PopupScanner requires browser support for the
[<dialog> element](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/dialog), a modern
Web API which has been widely available since early 2022.
# Supported Symbologies
STRICH supports linear (1D) and matrix (2D) symbologies.
* [Linear (1D) symbologies](1d-linear-barcodes.html)
* [Matrix (2D) symbologies](2d-matrix-codes.html)
Note:
In typical use cases, only one or two symbologies are required. For best performance, we recommend only enabling the symbologies needed
using the [symbologies](https://docs.strich.io/reference/interfaces/EngineConfiguration.html#symbologies)
configuration parameter.
# 1D Linear Barcodes
STRICH supports the following linear (1D) barcode symbologies:
* [Code 128](code-128.html)
* [EAN/UPC](ean-upc.html)
* [ITF (Interleaved-2-of-5)](itf-interleaved-2-of-5.html)
* [Code 39](code-39.html)
* [Code 93](code-93.html)
* [GS1 DataBar](gs1-databar.html)
* [Codabar](codabar.html)
* [MSI Plessey](msi-plessey.html)
# Code 128
Code 128 is a compact, variable-length linear barcode symbology, developed in 1981 and standardized as ISO/IEC 15417.
It is used extensively in logistics, healthcare, transportation, and the most widely used 1D symbology outside of retail.
A sample Code 128 barcode is shown below.

## Symbology Characteristics
| Configuration name |`code128` |
| ISO specification |ISO/IEC 15417:2007 |
| Encodable character set |All 128 ISO/IEC 646 (ASCII) characters, i.e. characters 0 to 127 inclusive, and characters with byte values 128-255 |
| Integrity protection |Mandatory checksum character |
| Quiet zone |Leading and trailing quiet zone of ten modules (10X) is required |
## Configuration Options
| Option |Description |Sample Configuration |Default |
------------------------------------------------------
| minLen |The minimum length of scanned codes | ```JAVASCRIPT const config = { engine: { symbologies: [ { name: "code128", minLen: 8 } ] } } ``` |4 |
| maxLen |The maximum length of scanned codes | ```JAVASCRIPT const config = { engine: { symbologies: [ { name: "code128", maxLen: 16 } ] } } ``` |32 |
| qz |The minimum size of the quiet zone, in modules (X) | ```JAVASCRIPT const config = { engine: { symbologies: [ { name: "code128", qz: 10 } ] } } ``` |5 |
## GS1-128 (formerly UCC/EAN-128)
GS1-128 is a subset of a Code 128 which encodes structured
information in Application Identifiers (AIs), such as a GTIN, a lot number, etc.
For example, the GS1-128 barcode below encodes a GTIN `40614141006364`,
a production date `220101` (YYMMDD) in [AI 11](https://ref.gs1.org/ai/11) of and a batch or lot number `A1B2C3D4`
in [AI 10](https://ref.gs1.org/ai/10).

# EAN/UPC
EAN and UPC barcodes are the most commonly used barcodes in retail applications.
While EAN barcodes is used on product packaging in Europe and worldwide, UPC
barcodes are typically used in the United States and Canada.
Although introduced as early as 1974 (it recently celebrated its 50th anniversary),
the EAN/UPC barcodes and remain in widespread use. GS1 – the global standards organization – mandates their replacement as part of the
[Sunrise 2027 initiative](https://www.gs1us.org/industries-and-insights/by-topic/sunrise-2027),
but it is unlikely that these barcodes will disappear anytime soon.
Both EAN and UPC codes exist in full (EAN-13, UPC-A) and shortened (EAN-8, UPC-E) variants.
The table below shows samples for each of the variants.
| Symbology |Sample barcode |
-----------------------------
| EAN-13 (`ean13`) | |
| EAN-8 (`ean8`) | |
| UPC-A (`upca`) | |
| UPC-E (`upce`) | |
## Symbology Characteristics
| Configuration name | `ean13`, `ean8`, `upca`, `upce` |
| ISO specification |ISO/IEC 15420 |
| Encodable character set |Numeric (0-9) |
| Integrity protection |One mandatory checksum character |
| Quiet zone | The size of the quiet zone in modules (X) depends on the variant: EAN-13: left 11X, right 7X, EAN-8: 7X, UPC-A: 9X, UPC-E: left 9X, right 7X |
## Configuration Options
EAN/UPC barcodes have a fixed length of 13 digits, so the `minLen` and `maxLen` options are not available.
### EAN-13
| Option |Description |Sample Configuration |Default |
------------------------------------------------------
| qz |The minimum size of the quiet zone, in modules (X) | ```JAVASCRIPT const config = { engine: { symbologies: [ { name: "ean13", qz: 10 } ] } } ``` |3 |
### EAN-8
EAN-8 barcodes have a fixed length of 8 digits, so the `minLen` and `maxLen` options are not available.
| Option |Description |Sample Configuration |Default |
------------------------------------------------------
| qz |The minimum size of the quiet zone, in modules (X) | ```JAVASCRIPT const config = { engine: { symbologies: [ { name: "ean8", qz: 10 } ] } } ``` |3 |
### UPC-A
UPC-A barcodes have a fixed length of 12 digits, so the `minLen` and `maxLen` options are not available.
| Option |Description |Sample Configuration |Default |
------------------------------------------------------
| qz |The minimum size of the quiet zone, in modules (X) | ```JAVASCRIPT const config = { engine: { symbologies: [ { name: "upca", qz: 10 } ] } } ``` |4 |
### UPC-E
UPC-E barcodes have a fixed length of 8 digits, so the `minLen` and `maxLen` options are not available.
| Option |Description |Sample Configuration |Default |
------------------------------------------------------
| qz |The minimum size of the quiet zone, in modules (X) | ```JAVASCRIPT const config = { engine: { symbologies: [ { name: "upce", qz: 10 } ] } } ``` |3 |
# ITF (Interleaved-2-of-5)
Interleaved-2-of-5 is a variable-length linear symbology that is used primarily
in logistics and retail and encodes an even number of numerical characters.
A sample Interleaved-2-of-5 barcode is shown below.

## Symbology Characteristics
| Configuration name |`i25` |
| ISO specification |ISO/IEC 16390 |
| Encodable character set |Numeric (0-9) |
| Integrity protection |One optional checksum character |
| Quiet zone |Leading and trailing quiet zone of ten modules (10X) is required |
## Configuration Options
| Option |Description |Sample Configuration |Default |
------------------------------------------------------
| minLen |The minimum length of scanned codes | ```JAVASCRIPT const config = { engine: { symbologies: [ { name: "i25", minLen: 8 } ] } } ``` |6 |
| maxLen |The maximum length of scanned codes | ```JAVASCRIPT const config = { engine: { symbologies: [ { name: "i25", maxLen: 16 } ] } } ``` |32 |
| qz |The minimum size of the quiet zone, in modules (X) | ```JAVASCRIPT const config = { engine: { symbologies: [ { name: "i25", qz: 10 } ] } } ``` |5 |
## ITF-14
ITF-14 is a GS1 subset of Interleaved-2-of-5 which encodes a GTIN-14.
The sample barcode below encodes a 14-digit GTIN and shows the horizontal
and vertical bearer bars surrounding the barcode that are required for ITF-14.

If you are scanning ITF-14 barcodes exclusively, set the minimum and maximum length to 14 to avoid short scans.
```JAVASCRIPT
const config = {
engine: {
symbologies: [
{ name: "i25", minLen: 14, maxLen: 14 }
]
}
}
```
# Code 39
Code 39 is a variable-length linear barcode symbology, developed in 1974 and standardized as ISO/IEC 16388.
The data density is comparatively low, so it requires considerably more space to encode the same amount of data
and can not be used to label smaller items.
Code 39 is used in inventory and item tracking applications as well as some postal services.
A sample Code 39 barcode is shown below.

## Symbology Characteristics
| Configuration name |`code39` |
| ISO specification |ISO/IEC 16388 |
| Encodable character set |Alphanumeric (A-Z) and numeric (0-9) as well as $ % + - . / and space characters |
| Integrity protection |Optional mod 43 check digit |
| Quiet zone |Leading and trailing quiet zone of at least ten modules (10X) is required |
## Optional Check Digit
Code 39 supports an optional check digit for enhanced data security (ISO/IEC 16388:2007, A.1 Check Character).
The check digit is placed immediately after the final data character and before the stop character.
By default, check digit verification is disabled (`checksumMode` is set to `0`) and all
characters, including the check digit, will be returned when the barcode is read. If the SDK is configured to validate the check digit, it can either be stripped (`1`)
or transmitted (`2`).
Tip:
Please note that if you enable check digit verification, Code 39 barcodes that do not contain a valid
check digit will no longer be readable. If you intend to read Code 39 barcodes with and without
check digits, you have to implement the check digit validation yourself. Code 39 does not indicate the
presence of a check digit in any way, so there is no reliable way to detect if a barcode has
a check digit or not, unless you are using fixed-length barcodes.
### Example: Code 39 barcode with check digit
In the barcode below, the R character is a check digit and the encoded data is CODE 39. The asterisk characters
denote start and stop characters and are typically included in the human-readable interpretation but not
transmitted by the decoder.

## Configuration Options
| Option |Description |Sample Configuration |Default |
------------------------------------------------------
| minLen |The minimum length of scanned codes | ```JAVASCRIPT const config = { engine: { symbologies: [ { name: "code39", minLen: 8 } ] } } ``` |4 |
| maxLen |The maximum length of scanned codes | ```JAVASCRIPT const config = { engine: { symbologies: [ { name: "code39", maxLen: 16 } ] } } ``` |32 |
| qz |The minimum size of the quiet zone, in modules (X) | ```JAVASCRIPT const config = { engine: { symbologies: [ { name: "code39", qz: 10 } ] } } ``` |5 |
| checksumMode | Check digit (mod 43) processing mode: 0: no check digit expected 1: validate and strip check digit 2: validate and transmit check digit | ```JAVASCRIPT const config = { engine: { symbologies: [ { name: "code39", checksumMode: 1 } ] } } ``` |0 |
# Code 93
Code 93 is a variable-length linear barcode symbology, developed in 1982 and standardized in the AIM-BC5-2000
specification. It was designed to provide higher density and better security than the
[Code 39](code-39.html) symbology.
The data density is comparatively low, so it requires considerably more space to encode the same amount of data
and can not be used to label smaller items.
A sample Code 93 barcode is shown below.

## Symbology Characteristics
| Configuration name |`code93` |
| AIM specification |AIM-BC5-2000 |
| Encodable character set |All 128 ASCII characters |
| Integrity protection |Two mandatory check characters |
| Quiet zone |Leading and trailing quiet zone of at least ten modules (10X) is required |
## Configuration Options
| Option |Description |Sample Configuration |Default |
------------------------------------------------------
| minLen |The minimum length of scanned codes | ```JAVASCRIPT const config = { engine: { symbologies: [ { name: "code93", minLen: 8 } ] } } ``` |4 |
| maxLen |The maximum length of scanned codes | ```JAVASCRIPT const config = { engine: { symbologies: [ { name: "code93", maxLen: 16 } ] } } ``` |32 |
| qz |The minimum size of the quiet zone, in modules (X) | ```JAVASCRIPT const config = { engine: { symbologies: [ { name: "code93", qz: 10 } ] } } ``` |5 |
# GS1 DataBar
GS1 DataBar is a family of linear symbologies with both fixed-length
and variable-length and stacked variants. DataBar is commonly used in
retail settings, for product identification and coupon codes.
Previously DataBar was also known as Reduced Spaced Symbology (RSS),
but the name was eventually abandoned because of a name clash with
another technology also named RSS: Really Simple Syndication.
GS1 DataBar exists in a number of variants:
* DataBar Omnidirectional encodes a GS1 GTIN
* DataBar Expanded encode arbitrary GS1 AIs.
* Stacked versions of both DataBar and DataBar Expanded exist to condense the information into a more square shape.
The main use case of DataBar is adding additional product information apart from the GTIN, like weight or expiry date.
DataBar barcodes are often found on fresh produce.
| Symbology |Sample barcode |
-----------------------------
| DataBar Omnidirectional (`databar`) | |
| DataBar Omnidirectional Stacked (`databar`) | |
| DataBar Expanded (`databar-exp`) | |
| DataBar Expanded Stacked (`databar-exp`) | |
## Symbology Characteristics
| Configuration name | `databar`, `databar-exp` Stacked variants are automatically enabled. |
| ISO specification |ISO/IEC 24724 |
| Encodable character set | DataBar: Numeric (0-9) DataBar Expanded: Numeric and alphanumeric characters, 20 punctuation characters. |
| Integrity protection |One mandatory checksum character |
| Quiet zone |No quiet zones are required |
## Configuration Options
DataBar and DataBar Expanded symbologies do not have symbology-specific configuration options.
## Limitations
* The DataBar Limited symbology is not supported. Please [contact us](mailto:hello@pixelverse.ch) if you require DataBar Limited support.
* 2D component linkage is not supported
# Codabar
Codabar is a variable-length linear barcode symbology developed in 1972 and standardized in the AIM-BC3-2000
specification.
STRICH implements Rationalized Codabar, which uses a fixed ratio of 3:1 for the wide and narrow
elements, as recommended in the AIM-BC3-2000 specification. Previous specifications such as the original
Pitney-Bowes specification used a fixed character width and varying wide-narrow ratios.
Codabar is widely used in libraries. A sample Codabar barcode is shown below.

## Symbology Characteristics
| Configuration name |`codabar` |
| AIM specification |AIM-BC3-2000 |
| Encodable character set |Numeric (0-9), six special characters (-, $, :, /, ., +) and four start/stop characters (A, B, C, D) that are transmitted as well as they are used to distinguish applications. |
| Integrity protection |Optional mod 16 check digit |
| Quiet zone |Leading and trailing quiet zone of at least ten modules (10X) is required |
## Optional Check Digit
Codabar supports an optional check character for enhanced data security (AIM-BC3-2000, E.3 A Check Character Format).
The check digit is placed immediately after the final data character and before the stop character.
By default, check digit verification is disabled (`checksumMode` is set to `0`) and all
characters, including the check digit, will be returned when the barcode is read. If the SDK is configured to validate the check digit, it can either be stripped (`1`)
or transmitted (`2`).
Tip:
Please note that if you enable check digit verification, Codabar barcodes that do not contain a valid
check digit will no longer be readable. If you intend to read Codabar barcodes with and without
check digits, you have to implement the check digit validation yourself. Codabar does not indicate the
presence of a check digit in any way, so there is no reliable way to detect if a barcode has
a check digit or not, unless you are using fixed-length barcodes.
### Example: Codabar barcode with check digit
The barcode belows encodes the value A1234567890A and includes a check digit that is not visible
in the human-readable interpretation below the barcode.

If check digit verification is disabled, the barcode will read as A12345678903A with the final 3
being the check digit.
## Configuration Options
| Option |Description |Sample Configuration |Default |
------------------------------------------------------
| minLen |The minimum length of scanned codes | ```JAVASCRIPT const config = { engine: { symbologies: [ { name: "codabar", minLen: 8 } ] } } ``` |4 |
| maxLen |The maximum length of scanned codes | ```JAVASCRIPT const config = { engine: { symbologies: [ { name: "codabar", maxLen: 16 } ] } } ``` |32 |
| qz |The minimum size of the quiet zone, in modules (X) | ```JAVASCRIPT const config = { engine: { symbologies: [ { name: "codabar", qz: 10 } ] } } ``` |5 |
| checksumMode | Check digit (mod 10) processing mode: 0: no check digit expected 1: validate and strip check digit 2: validate and transmit check digit | ```JAVASCRIPT const config = { engine: { symbologies: [{name: "codabar", checksumMode: 1}] } } ``` |0 |
## Start/Stop Characters
Codabar uses four distinct start/stop characters, `A`, `B`, `C` and `D`. Although the AIM specification says
otherwise, the start/stop characters are often not included in the human-readable interpretation
printed below the barcode.

The SDK includes start/stop characters in its output, as they can be used for application-specific purposes.
Start and stop characters also count towards the barcode length and need to be
taken into consideration when using `minLen` and/or `maxLen` to restrict the
acceptable barcode length.
### Example: Codabar library barcode
The Codabar barcode below encodes the value `A34444913793584B`, with `A` used as start
character and `B` as stop character. The total length of the barcode is `16`.

To restrict the SDK to only these types of barcodes, you could set `minLen` and `maxLen`
to the `16`.
```JAVASCRIPT
const config = {
engine: {
symbologies: [
{ name: "codabar", minLen: 16, maxLen: 16 }
]
}
}
```
# MSI Plessey
MSI Plessey is a variable-length linear barcode symbology developed around 1970.
STRICH implements Rationalized Codabar, which uses a fixed ratio of 3:1 for the wide and narrow
elements, as recommended in the AIM-BC3-2000 specification. Previous specifications such as the original
Pitney-Bowes specification used a fixed character width and varying wide-narrow ratios.
MSI Plessey is not used widely anymore, but can still be found in inventory control applications and on shelf tags.
A sample MSI Plessey barcode is shown below.

## Symbology Characteristics
| Configuration name |`msi-plessey` |
| Encodable character set |Numeric (0-9) |
| Integrity protection |Modulo 10 check digit (optional, but highly recommended) |
| Quiet zone |Leading and trailing quiet zone of at least ten modules (10X) is recommended |
## Optional Check Digit
MSI Plessey supports an optional check character for more reliable scanning. The check digit is placed immediately after the final data character and before the stop character.
By default, check digit verification is enabled and the check digit is stripped from the returned data (`checksumMode` is set to `1`).
Warning:
The MSI Plessey symbology is not self-checking, it relies heavily on the check digit for reliable reading. Using a check digit is strongly recommended.
## Restricting Minimum and Maximum Length
In addition to using a check digit, we also strongly recommend to configure `minLen` and `maxLen` to your use case.
Most applications of MSI Plessey use fixed-length barcodes. For example, if all your barcodes encode eight digits, setting `minLen` and `maxLen` to `8` is recommended to prevent misreads and improve decoding efficiency.
## Configuration Options
| Option |Description |Sample Configuration |Default |
------------------------------------------------------
| minLen |The minimum length of scanned codes | ```JAVASCRIPT const config = { engine: { symbologies: [ { name: "msi-plessey", minLen: 8 } ] } } ``` |6 |
| maxLen |The maximum length of scanned codes | ```JAVASCRIPT const config = { engine: { symbologies: [ { name: "msi-plessey", maxLen: 8 } ] } } ``` |12 |
| qz |The minimum size of the quiet zone, in modules (X) | ```JAVASCRIPT const config = { engine: { symbologies: [ { name: "msi-plessey", qz: 10 } ] } } ``` |10 |
| checksumMode | 0: no check digit expected 1: single Modulo 10 check digit validated and stripped 2: single Modulo 10 check digit validated and transmitted | ```JAVASCRIPT const config = { engine: { symbologies: [{name: "msi-plessey", checksumMode: 1}] } } ``` |0 |
# 2D Matrix Codes
STRICH supports the following matrix (2D) code symbologies:
* [QR Code](qr-code.html)
* [Data Matrix](data-matrix.html)
* [Aztec Code](aztec-code.html)
* [PDF417](pdf417.html)
# QR Code
QR Code is the most well-known 2D matrix symbology, originally developed in 1994 by Denso Wave,
it is used in a wide variety of industries and applications. It is now standardized as ISO/IEC 18004.
A sample QR Code is shown below:

A reduced variant called Micro QR Code is defined in the same ISO specification and also supported by SDK versions 1.12.0 and later.
A sample Micro QR Code is shown below:

## Symbology Characteristics
| Configuration name | `qr` (regular QR Code), `microqr` (Micro QR Code) |
| ISO specification |ISO/IEC 18004 |
| Encodable character set |All 8-bit values can be encoded. |
| Capacity |QR Code symbols can encode up to 7'089 numeric characters, 4'296 alphanumeric characters, 2'953 bytes of binary data or 1'817 kanji characters for a version 40 symbol at the lowest error correction level. Micro QR Codes are limited to 35 numeric, 21 alphanumeric, 15 bytes of binary data or 9 kanji characters for an M4 version symbol at the lowest error correction level. |
| Integrity protection |Reed-Solomon error correction, selectable (four levels, three for Micro QR Code) |
| Quiet zone | A quiet zone of four modules (4X) is required around the symbol. Micro QR Codes require a 2X quiet zone. |
QR Code is a registered trademark of [DENSO WAVE INCORPORATED](http://www.denso-wave.com/en/).
## Configuration Options
### QR Code
| Option |Description |Sample Configuration |Default |
------------------------------------------------------
| roundedFPs |Toggle recognition of QR codes with rounded finder patterns | ```JAVASCRIPT const config = { engine: { symbologies: [ { name: "qr", roundedFPs: true } ] } } ``` |false |
| curved |Toggle recognition of QR codes on curved surfaces | ```JAVASCRIPT const config = { engine: { symbologies: [ { name: "qr", curved: true } ] } } ``` |false |
Warning:
Enabling recognition of QR codes with rounded finder patterns forces the library to use less efficient algorithms.
We do not recommend using QR codes that are not compliant with the ISO specification. The edges of the square finder patterns are useful in determining
the location of adjacent finder patterns, and help in mapping the QR Code grid.
### Micro QR Code
Micro QR Code does not have any symbology-specific configuration options.
## Limitations
The following elements of the ISO specification are not implemented:
* ECI (Extended Channel Interpretation)
* Model 1 QR Codes
Tip:
The term “Model 1” is distinct from “Version 1”. Somewhat confusingly, the version of a QR code denotes its size.
A version 1 QR code has a size of 21x21 modules.
ISO/IEC 18004:2006 stopped mentioning this first iteration of QR Code in 2006, and redefined QR Code to mean Model 2 QR Code, so we do not consider it relevant in today's landscape.

# Data Matrix
Data Matrix is a high-density 2D matrix symbology that can store large amounts of data in
a small area. Data Matrix codes are used to label small items.
A sample Data Matrix Code is shown below.

## Symbology Characteristics
| Configuration name |`datamatrix` |
| ISO specification |ISO/IEC 16022 |
| Encodable character set |All 8-bit values can be encoded. |
| Capacity |The largest symbol can encode up to 3116 numeric characters, 2335 alphanumeric characters, or 1555 bytes of binary data. |
| Integrity protection |Reed-Solomon error correction (ECC200). ECC levels 000-140 are not supported. |
| Quiet zone |A quiet zone of one module (1X) is required around the symbol. |
## Configuration Options
Data Matrix does not have any symbology-specific configuration options.
## Limitations
The following elements of the ISO specification are not implemented:
* Non-square Data Matrix codes
* Structured Append
* ECI (Extended Channel Interpretation)
* ECC 000-140 convolution-based error correction (only ECC 200 is supported)
# Aztec Code
Aztec Code is a high-density 2D matrix symbology that can store large amounts of data in
a small area and does not require a quiet zone. Aztec codes are commonly used in ticketing,
for instance railway tickets or airline boarding passes.
The name derives from the unique shape of the central finder pattern of concentric squares, which is
reminiscent of an Aztec pyramid viewed from above.
A sample Aztec Code is shown below.

Aztec codes are frequently seen on mobile airline boarding passes. On printed boarding passes,
[PDF417](pdf417.html) is more commonly used. A sample boarding pass stored in Apple Wallet is shown below.

## Symbology Characteristics
| Configuration name |`aztec` |
| ISO specification |ISO/IEC 24788:2008 |
| Encodable character set |All 8-bit values can be encoded. |
| Capacity |The largest symbol can encode up to 3832 numeric or 3067 alphanumeric characters, or 1914 bytes of binary data. |
| Integrity protection |Aztec Code has strong Reed-Solomon error correction for both the inner part (bullseye) and the surrounding payload. |
| Quiet zone |A quiet zone is not required. |
## Configuration Options
Aztec Code does not have any symbology-specific configuration options.
## Limitations
The following elements of the ISO specification are not implemented:
* Structured Append
* Reader Initialization Symbols
* Aztec Runes
* ECI (Extended Channel Interpretation)
# PDF417
PDF417 is a stacked 2D barcode symbology. It was designed to be read
by conventional 1D barcode scanners in multiple passes, and is composed
of multiple 1D barcodes stacked over each other.
PDF417 are often found on airline boarding passes and driving licenses.
A sample PDF417 barcode is shown below.

## Symbology Characteristics
| Configuration name |`pdf417` |
| ISO specification |ISO/IEC 15438 |
| Encodable character set |Three different compaction modes to efficiently encode numeric, alphanumeric and binary data. |
| Capacity |The largest symbol can encode up to 3116 numeric characters, 2335 alphanumeric characters, or 1555 bytes of binary data. |
| Integrity protection |Reed-Solomon error correction, with ten error correction levels (0-9) |
| Quiet zone |A quiet zone of two modules (2X) is required around the symbol. |
## Configuration Options
PDF417 does not have any symbology-specific configuration options.
## Limitations
The following elements of the ISO specification are not implemented:
* Macro PDF417 (distributing data across multiple codes)
* ECI (Extended Channel Interpretation)
## Related Material
Tip:
Tutorial: [Scanning PDF417 Barcodes on US Driving Licenses](scanning-pdf417-barcodes-on-us-driving-licenses-for-age-verification.html)
# Deployment Guide
Advice on deploying an app that uses STRICH SDK to a hosting service.
## Serving From a Secure Origin
STRICH accesses the smartphone camera using the web browser's
[getUserMedia](https://developer.mozilla.org/en-US/docs/Web/API/MediaDevices/getUserMedia#privacy_and_security) API.
This API can only be used in [secure contexts](https://developer.mozilla.org/en-US/docs/Web/Security/Secure_Contexts),
meaning your app needs to be served from an HTTPS, file:/// or localhost URL.
Many developers of mobile web apps use tools like ngrok to expose a publicly accessible HTTPS endpoint with
automatically generated TLS certificate.
For local development, web frameworks such as Angular usually include a built-in web server, which will default to
serving the app on localhost using HTTP, not HTTPS. There are usually options that can be supplied to control the
interface the server binds and TLS support. Some examples for popular frameworks are included below.
Tip:
Tools like [ngrok](https://ngrok.com/use-cases#development) or
[DevTunnels](https://learn.microsoft.com/en-us/azure/developer/dev-tunnels/overview) make developing scanning apps much simpler, as they provide a publicly reachable
endpoint with automatic SSL. This is very useful for testing a locally hosted app on a smartphone.
### Angular
```SHELL
ng serve --host 0.0.0.0 --ssl
```
### Parcel
```SHELL
parcel src/index.html --https
```
### React
```SHELL
HTTPS=true npm start
```
### Vite / Astro
The [Vite documentation](https://vite.dev/config/server-options.html#server-https)
suggests using the `@vitejs/plugin-basic-ssl` plugin.
```JAVASCRIPT
import { defineConfig } from 'astro/config';
import basicSsl from '@vitejs/plugin-basic-ssl'
// https://astro.build/config
export default defineConfig({
vite: {
plugins: [basicSsl()],
server: {
https: true
},
},
});
```
Binding to the public IP address of the network interface can be done via the `--host` CLI parameter:
```SHELL
npm run dev -- --host
```
### Vue
[Add https: true field to the vue.config.js file](https://stackoverflow.com/questions/45807049/how-to-run-vue-js-dev-serve-with-https/50741529#50741529).
## License Key Management
Even though it is impossible to fully secure a client-side license key, we recommend the following best practices:
* If your app requires authentication, expose the license key only on authenticated routes.
* If your app has a dynamic configuration mechanism, store your license key there instead of hard-coding it in your app. This will make it easier to exchange your license key if it expires (offline keys) or in case it's compromised.
Note:
For offline license keys, we highly recommend to manage the license key in a dynamic configuration mechanism, so it can be exchanged without redeploying the application when it expires.
## Firewall Configuration
Note:
This applies only to Basic and Professional plans. Enterprise licenses optionally allow fully offline operation.
For Basic and Professional licenses, an online license check is performed via an HTTPS request. If you are running in
a restricted environment, make sure that HTTPS connections to hosts *.strich.io are allowed, otherwise the SDK can
not be initialized.
## Content Security Policy (CSP)
Note:
This applies only to Basic and Professional plans. Enterprise licenses optionally allow fully offline operation.
When apps are being served with a Content Security Policy, a few entries are needed for STRICH license checking to work.
| Directive |Values |Description |
----------------------------------
| connect-src | *.strich.io, data: | Required for license verification and analytics, as well as loading embedded Web Assembly from data URLs. |
| script-src | wasm-unsafe-eval, unsafe-eval | Required for Web Assembly execution. Older browsers may not yet support wasm-unsafe-eval, and therefore require unsafe-eval. |
| img-src | data: | Required for STRICH to load embedded image assets such as icons. |
| media-src | data: | Required for STRICH to load embedded sound assets such as the beep sound played in [audio feedback](https://docs.strich.io/reference/interfaces/FeedbackConfiguration.html#audio). |
## User-Agent Client Hints (optional)
Tip:
This step is optional, but necessary if you would like to see detailed statistics about device models in the Customer Portal.
Recent versions of Chrome (starting mid-2023) and browsers based on Chrome (i.e. Edge) include a privacy feature
called User-Agent reduction which limits the amount of data a browser sends as part of its requests, especially the
User-Agent HTTP header. Instead of the device model, e.g. `SM-G991` for a Samsung Galaxy S21, Chrome will now just
send the value `K`, hiding the actual device model.
Websites have the ability to ask the browser for additional information, so-called Client hints. This is commonly done
by including an `Accept-CH` header in the response serving the website. Alternatively, and often easier to achieve
for application developers, Client Hints can also be requested by including Accept-CH in a meta tag of the
application's HTML code:
```HTML
```
To pass the Client Hints to STRICH for more detailed statistics, you need to delegate them. STRICH infrastructure is a
third-party origin ― the first party being the origin that serves the app (you).
To delegate the Client Hints to STRICH, also add the following snippet to the HEAD section of your HTML code:
```HTML
```
After you put both mechanisms in place (requesting Client Hints, and delegating them to STRICH), you should start
seeing detailed device models in the usage stats.
## Optimizing Online License Check (optional)
Tip:
This applies only to Basic and Professional plans. Enterprise licenses optionally allow fully offline operation.
During initialization, the SDK performs a check of the license key by contacting the license service. You can provide a hint to the web browser
that will cause it to
[preemptively perform the DNS lookup](https://developer.mozilla.org/en-US/docs/Web/Performance/dns-prefetch)
for the license service, making the check run faster in some cases.
To enable DNS-prefetching, add the following snippet to the HEAD section of your HTML code:
```HTML
```
If the license check was successful, the decision is stored locally for up to 48 hours, allowing for intermittent offline use.
# Best Practices for Scanning Apps
This guide contains a collection of advice for building great scanning apps.
While no two apps are the same, we believe that the recommendations in this guide are pretty universal.
## Configure only the symbologies you need
Typically scanning apps only scan certain types of barcodes. It is therefore important to enable only those
types, and to avoid enabling types that are not needed. This avoids unnecessary image processing and
computation dedicated to locating and decoding these types.
### Example: setting supported symbologies to Code 128 linear barcodes and QR codes:
```JAVASCRIPT
const configuration = {
// ...
engine: {
symbologies: [ "code128", "qr" ]
}
// ...
};
```
[EngineConfiguration API Reference](https://docs.strich.io/reference/interfaces/EngineConfiguration.html#symbologies)
For variable-length symbologies like Code 128, Code 39 etc. it is also advisable to set
`minLen` and `maxLen` according to your use case. For instance if you are scanning Code 128 barcodes
with a length of between 8 and 10 characters, you could further restrict the configuration:
### Example: setting minLen/maxLen for variable-length symbologies
```JAVASCRIPT
const configuration = {
// ...
engine: {
symbologies: [ { name: "code128", minLen: 8, maxLen: 10 } ]
}
// ...
};
```
## Enable audio and vibration feedback
Audio and vibration user feedback is not a gimmick, it is an important part of the scanning user experience.
There's nothing that can replace that satisfying beep after scan.
```JAVASCRIPT
const configuration = {
// ...
feedback: {
audio: true,
vibration: true
}
// ...
};
```
[FeedbackConfiguration API Reference](https://docs.strich.io/reference/interfaces/FeedbackConfiguration.html)
## Choose an appropriate region of interest
The region of interest specifies the extent of the video feed where STRICH should to locate and decode barcodes.
The larger this region is, the more time is spent in computation, and the longer it may take for a barcode to be
detected.
Humans have a tendency to center the object of interest in their field of view. This principle (let's call it
centrality) can also be applied to a camera feed. Make sure the region of interest is centered and its extent
is appropriate for the barcode type. The region of interest is bounded by a viewfinder in the overlay, users
will naturally try to center the barcode in the viewfinder.
### Example 1: narrow region of interest, suitable for 1D barcodes
```JAVASCRIPT
const configuration = {
// ...
locator: {
regionOfInterest: {
// restrict active area to a horizontal bar in the center
left: 0.05,
right: 0.05,
top: 0.4,
bottom: 0.4
}
}
// ...
};
```

### Example 2: square region of interest, suitable for 2D barcodes
```JAVASCRIPT
const configuration = {
// ...
locator: {
regionOfInterest: {
// restrict active area to a square-shaped region in the center
left: 0.2,
right: 0.2,
top: 0.3,
bottom: 0.3
}
}
// ...
};
```

[LocatorConfiguration API Reference](https://docs.strich.io/reference/interfaces/LocatorConfiguration.html#)
## Adding manual entry capability
There is always a possibility that a barcode type is supported by STRICH but can't be scanned because it is too
damaged or otherwise illegible. Adding a fallback option to input a code manually using the smartphone's on-screen
keyboard is therefore always recommended, especially for critical applications.
A subtle keyboard icon that toggles visibility of an input field, and focuses the field so the keyboard pops up
usually works fine.
# Integration Guides
# Angular Integration Guide
This guide contains common patterns and advice on how to best integrate STRICH into an app built with
the [Angular](https://angular.dev) framework.
## SDK Initialization
SDK initialization should be done as early as possible, and only once. Angular apps usually contain a root component
(named `AppComponent` by default). The AppComponent's `ngOnInit` is a convenient place to initialization the SDK,
even better is to use a Service to encapsulate interaction with the SDK.
## Using a Service
If you intend to read barcodes in multiple screens and not just in a single component, it makes sense to move common
code into a [Service](https://angular.dev/guide/di/creating-injectable-service). Good candidates for putting in a Service are:
Initialization
: [Singleton services](https://angular.dev/guide/ngmodules/singleton-services) are initialized once, which makes them a good fit
for initialization (which also happens only once).
Scanning results
: Using an `Observable` in the Service allows propagating scanned codes to components.
Configuration
: Sharing common parts of `BarcodeReader` configuration avoids duplication.
Our Angular sample contains an example of such a Service:
[ScannerService](https://github.com/pixelverse-llc/strich-angular-sample/blob/main/src/app/services/scanner.service.ts)
## Using route guards to require SDK initialization for routes that need scanning
Using a [route guard](https://angular.dev/guide/routing/common-router-tasks#preventing-unauthorized-access) is an idiomatic way of requiring
certain conditions to be met before accessing a part of your app.
Screens requiring a `BarcodeReader` rely on the SDK to be initialized. This can be achieved by putting a guard on the
route that checks the SDK initialization status.
Our Angular sample contains an example of a guard that will only permit navigating to a route if the SDK
is initialized:
[sdkInitialized guard](https://github.com/pixelverse-llc/strich-angular-sample/blob/main/src/app/guards/strich-scanning-guards.ts)
## Initializing BarcodeReader in ngAfterViewInit
The `BarcodeReader` requires a host element with known size to be present. An Angular component typically provides the
host element in its template. Using the `ngAfterViewInit` lifecycle hook ensures that the component has finished
initializing its view and child views, and is therefore an appropriate place to initialize the `BarcodeReader`.
* [Angular component lifecycle](https://angular.dev/guide/components/lifecycle)
Tip:
The `ngOnInit` lifecycle hook can not be used to initialize a `BarcodeReader`, as the host element must be present
and its size known, and that is only the case after `ngAfterViewInit` has run.
## Calling destroy() when BarcodeReader is no longer needed
Components that include a `BarcodeReader` in their content need to destroy the `BarcodeReader` instance when they no
longer intend to use it, otherwise the camera can remain in use, causing subsequent initializations to fail.
The [ngOnDestroy](https://angular.dev/guide/components/lifecycle#ngondestroy) component lifecycle hook is an appropriate place for calling
[BarcodeReader.destroy()](https://docs.strich.io/reference/classes/BarcodeReader.html#destroy), as it is
invoked when the component is destroyed by Angular, for instance during a navigation event.
## Serving your app during development
STRICH uses the smartphone camera to scan barcodes. Access to the smartphone camera is only available in secure origins,
so your app needs to be served over HTTPS.
The Angular development server (`ng serve`) supports serving via HTTPS using the `--ssl` command-line argument
Binding to 0.0.0.0 instead of localhost (the default) allows to easily test on a smartphone connected to the same
network as the developer machine.
```SHELL
ng serve --host 0.0.0.0 --ssl
```
If you are using the popular [ngrok](https://ngrok.com) tool to serve your app, you might instead serve over plain HTTP,
but then you need to disable checking of the `Host` header.
```SHELL
ng serve --disable-host-check
ngrok http 4200
```
Tip:
Tools like [ngrok](https://ngrok.com/use-cases#development) or
[DevTunnels](https://learn.microsoft.com/en-us/azure/developer/dev-tunnels/overview) make developing scanning apps much simpler, as they provide a publicly reachable
endpoint with automatic SSL. This is very useful for testing a locally hosted app on a smartphone.
## Sample Project
In addition to this guide, we also provide a [sample project](https://github.com/pixelverse-llc/strich-angular-sample)
showing how to integrate of the SDK into an Angular application. Some of the aspects that are shown are:
* Decoupling application logic from SDK and BarcodeReader by using a [Service](https://angular.dev/guide/di/creating-injectable-service)
* Wrapping BarcodeReader UI and lifecycle management into a [Component](https://angular.dev/guide/components)
* Using route guards to enforce preconditions (SDK is initialized) and postconditions (BarcodeReader is destroyed) when entering and leaving a route
# SvelteKit Integration Guide
We're working on it - hang in there!
In the meantime, check out our SvelteKit sample code repository on
[Github](https://github.com/pixelverse-llc/strich-sveltekit-sample).
# Vanilla JS Integration Guide
You don't need a framework to use STRICH!
Check out our sample code repository on [Github](https://github.com/pixelverse-llc/strich-javascript-sample) for an
example on how to use STRICH in plain JavaScript/HTML/CSS.
# Vue Integration Guide
We're working on it - hang in there!
In the meantime, check out our [Vue 2](https://github.com/pixelverse-llc/strich-vue2-sample)
and [Vue 3](https://github.com/pixelverse-llc/strich-vue3-sample) sample code repositories on
[Github](https://github.com/topics/strich-sdk).
# Salesforce Integration Guide
This guide describes how to integrate STRICH into Salesforce
[Lightning Web Components](https://developer.salesforce.com/developer-centers/lightning-web-components).
The information in this guide is BETA, please contact us directly if you wish to integrate with Salesforce.
## Loading the SDK from Static Resources
Due to restrictions on loading of third-party ES6 modules, a non-modular build of STRICH needs to be uploaded to
[Static Resources](https://developer.salesforce.com/docs/platform/lwc/guide/create-resources.html).
### Uploading non-modular STRICH build to Static Resources
At the time of writing, Lightning Web Components can not `import` ES6 modules from arbitrary URLs such as from a CDN.
They can import from other components, but the file size is limited to 128 KB, making it unsuitable for STRICH, which
currently bundles WebAssembly.
Upload the file `strich_noesm.js` as a static resource, do not add it to a zip archive beforehand:
* Name: `strich_noesm`
* Description: `Non-modular build of STRICH SDK`
* File:
After uploading, the static resource should look like this:

## Configuring Content-Security Policies (CSP)
CSP are accessed by entering Setup, then navigating to Settings > Security > Trusted URLs in the sidebar.
### Data URLs
STRICH embeds images and sounds, as well as WebAssembly code into its JavaScript bundle and loads these assets
via [Data URLs](https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/Data_URLs).
To enable loading from Data URLs, add a Trusted URL:
* URL: `data:`
* Directives: `connect-src`, `img-src` and `media-src`
### License Service
Note:
This step is not required for Enterprise licenses with the offline license check add-on.
To allow connections to the license service, add a Trusted URL:
* URL: `https://license.strich.io`
* Directives: `connect-src`
After setting up the trusted URLs, the dialog should look like similar to the screenshot below:

## Strict Security
If you run into CSP issues, please check that the Security Level setting for Content-Security Policies is set
to Strict CSP (see screenshot below).

## Sample Code
### WebComponent Template
```HTML
```
### WebComponent Logic
For production code, we recommend removing the console logging statements, but they can be useful during integration.
```JAVASCRIPT
import { LightningElement } from 'lwc';
import { loadScript } from 'lightning/platformResourceLoader';
import strich_noesm from '@salesforce/resourceUrl/strich_noesm';
export default class Scanner extends LightningElement {
errorMessage = null;
barcodeValue = '';
barcodeReader = null;
sdkState = '-';
sdkVersion = null;
constructor() {
super();
}
async connectedCallback() {
try {
// load STRICH SDK from static resources, requires non-modular build ('script tag')
await loadScript(this, strich_noesm);
this.sdkState = 'SDK loaded';
console.log(`Successfully loaded STRICH (noesm) from static resources`);
// from here on, global variable 'strich' is defined
await this.initializeSDK();
this.sdkState = 'SDK initialized';
const hostElem = this.template.querySelector('.strich-container');
this.barcodeReader = await this.initializeBarcodeReader(hostElem);
}
catch (error) {
console.error('STRICH SDK initialization failed', error);
this.errorMessage = error.message;
}
}
async disconnectedCallback() {
if (this.barcodeReader) {
await this.barcodeReader.destroy();
this.barcodeReader = null;
this.sdkState = 'BarcodeReader destroyed';
}
}
async initializeSDK() {
const licenseKey = '';
await strich.StrichSDK.initialize(licenseKey);
this.sdkVersion = strich.StrichSDK.version();
}
async initializeBarcodeReader(hostElem) {
const barcodeReader = new strich.BarcodeReader({
selector: hostElem,
frameSource: {
resolution: 'full-hd'
},
engine: {
symbologies: ['code128']
}
});
barcodeReader.detected = (detections) => {
this.barcodeValue = detections[0].data;
};
await barcodeReader.initialize();
this.sdkState = 'BarcodeReader initialized';
await barcodeReader.start();
this.sdkState = 'BarcodeReader started';
return barcodeReader;
}
}
```
### WebComponent Styling
```CSS
.strich-container {
min-height: 240px;
height: 240px;
background-color: black;
position: relative;
}
.strich-footer {
padding: 4px;
display: flex;
flex-direction: row;
gap: 10px;
justify-content: space-between;
}
.sdk-error-message {
color: red;
font-weight: bold;
}
```
# OutSystems Integration Guide
In collaboration with Stefan Nikolic, Senior Developer at Abeltech.eu.
## Forge Component
The preferred way to integrate STRICH into OutSystems apps is by using our
[Forge Component](https://www.outsystems.com/forge/component-overview/18265/strich-barcodereader-o11).
## Custom Integration
If you are for some reason unable to use the Forge component,
The rest of this guide focuses on custom JavaScript integrations.
### Adding STRICH SDK as a Script
Add the file `strich-noesm.js` from the NPM distribution or from a CDN to the module's scripts as described in
[Use JavaScript Code from an External Library](https://success.outsystems.com/documentation/11/extensibility_and_integration/javascript/extend_your_mobile_and_reactive_apps_using_javascript/use_javascript_code_from_an_external_library/).
Note that OutSystems does not seem to support loading ES6 modules, so it is important to use the non-modular build
instead of the regular `strich.js` distribution.

### Adding a BarcodeReader to a Screen
#### Adding a Container host element
The `BarcodeReader` instance needs a widget in which it will live. Add a Container element and give it a name, here
we used `hostElem`. Add the `position-relative` style class to the widget, so it will have relative CSS positioning,
which is required for `BarcodeReader`.

#### Initializing SDK and BarcodeReader in onReady
The [onReady](https://success.outsystems.com/documentation/11/developing_an_application/implement_application_logic/screen_and_block_lifecycle_events/#on-ready)
client action can be used to initialize the [SDK](https://docs.strich.io/reference/classes/StrichSDK.html#initialize)
and the [BarcodeReader](https://docs.strich.io/reference/classes/BarcodeReader.html#initialize).

In the client action, we call a custom JavaScript block that receives the ID of the host widget as an input parameter.
The ID is used to look up the DOM element, which is used to initialize the `BarcodeReader`. The script returns the
initialization Promise as an output parameter, which is stored in a local variable by the client action.
When a barcode is read, a client action is executed and passed the barcode data.
```JAVASCRIPT
const hostElem = document.getElementById($parameters.hostElementId);
const licenseKey = '';
$parameters.barcodeReader = strich.StrichSDK.initialize(licenseKey)
.then(() => {
const cfg = {
selector: hostElem,
engine: {
symbologies: ['code128', 'qr']
}
};
const barcodeReader = new strich.BarcodeReader(cfg);
return barcodeReader.initialize();
})
.then((barcodeReader) => {
barcodeReader.detected = (detections) => {
$actions.onBarcodeDetected(detections[0].data);
};
return barcodeReader.start().then(() => {
return barcodeReader;
});
});
```
Note:
The script currently returns the Promise that resolves into a BarcodeReader as an output parameter.
We are investigating ways to make this more seamless.
#### Tearing down BarcodeReader in onDestroy
The [onDestroy](https://success.outsystems.com/documentation/11/developing_an_application/implement_application_logic/screen_and_block_lifecycle_events/#on-destroy)
client action can be used to destroy the `BarcodeReader`, causing the camera to be released for subsequent use.

The onDestroy client action receives the local variable containing the `BarcodeReader` Promise as an input parameter,
and uses it to [destroy](https://docs.strich.io/reference/classes/BarcodeReader.html#destroy) the `BarcodeReader`
when the screen is exited.
```JAVASCRIPT
const barcodeReaderPromise = $parameters.barcodeReader;
barcodeReaderPromise
.then((barcodeReader) => {
return barcodeReader.destroy()
});
```
Note:
Destroying a `BarcodeReader` is an asynchronous action that should be awaited before the screen is left.
Currently this is not the case, as during a navigation from one screen to the next, the previous screen onDestroy
will only be called after the next screen's onReady is called. Also it is unclear if OutSystems supports asynchronous
lifecycle hooks. This means that the approach described in this guide does not currently support two consecutive
screens that both use a `BarcodeReader`.
# Advanced Topics
* [Reading GS1 Data in Barcodes](reading-gs1-data.html)
* [Customizing the Barcode Scanning UI](customizing-the-scanner-ui.html)
* [Non-Modular Build Flavor](non-modular-build-flavor.html)
* [Automated Testing](automated-testing.html)
* [Customizing the Scanner Sound](customizing-the-scanner-sound.html)
# Automated Testing
Automated testing of barcode scanning apps is not straightforward because of
the camera requirement. The camera is a physical sensor exposed as a shared
resource to the browser, with access gated by user-granted permissions.
There are multiple approaches to test webs apps that scan barcodes from the camera:
* Test the barcode scanning workflow end-to-end using a fake camera stream. This is the most consequent approach but may be difficult to set up depending on your environment.
* Inject or simulate a successfully scanned barcode: this typically requires dedicated code in your app to support running under test.
* Simulating manual entry of the barcode value by the user. This aligns with our recommendation to always provide [manual entry](best-practices-for-scanning-apps.html#adding-manual-entry-capability) as a fallback in case barcode scanning is not possible.
## Injecting a Fake Camera Stream using Playwright
We recommend using [Playwright](https://playwright.dev) for automated end-to-end
testing of web applications.
Playwright can be configured to start Chrome using special launch arguments
that provide a fake video stream to the browser.
For more information on this approach, please refer to the article
[Automated Testing of Barcode Scanning Apps with Playwright](https://strich.io/blog/posts/automated-testing-of-barcode-scanning-apps-with-playwright/) on
our blog.
Example: End-to-end testing of an ISBN Barcode Scan
The snippet below contains a Playwright script that tests scanning an ISBN
barcode with our [Demo App](https://demo.strich.io).
```TYPESCRIPT
import {test, expect, chromium} from '@playwright/test';
test('Scans EAN/UPC Book Barcode', async () => {
const browser = await chromium.launch({
args: [
"--use-fake-ui-for-media-stream",
"--use-fake-device-for-media-stream",
"--use-file-for-fake-video-capture=./test-videos/book_barcode.mjpeg",
]
});
const context = await browser.newContext({});
const page = await context.newPage();
// check start page is displayed
await page.goto('https://demo.strich.io/');
// select 1D retail barcodes and start scanning
await page.getByText("1D Retail").click();
await page.getByText("START SCANNING").click();
// fake video stream starts, barcode should be read
await expect(page.getByText('9781292223049')).toBeVisible();
// clean up
await context.close();
});
```
As the SDK considers the resolution of the video, the size of the host element and
the region of interest defined (if specified), you will get the best results if
the video file resolution has the same or similar aspect ratio as the browser viewport.
You can set a [device](https://playwright.dev/docs/emulation#devices) from
a list of built-in devices that Playwright supports on the test, or set a specific
[viewport](https://playwright.dev/docs/emulation#viewport) directly.
In most cases, adding a mobile browser project to your `playwright.config.ts` file
should be enough.
Example: Mobile Browser Project in Playwright Configuration
```JSON
export default defineConfig({
testDir: './tests',
// ... other properties omitted...
projects: [
{
name: 'Mobile Chrome', // use mobile viewport
use: {...devices['Pixel 7']},
}
]
});
``
```
# Reading GS1 Data
## Application Identifiers
GS1 is a global organization that sets standards for efficient and accurate supply chain management, with its
most notable contribution being the development of the barcode. Founded in 1974, GS1's mission is to enhance
the flow of goods and information across various industries by providing a universal system for product
identification and data sharing.
A key element of GS1's standards is the use of [Application Identifiers](https://ref.gs1.org/ai/) (AIs) in barcodes.
These identifiers allow specific data, such as batch numbers, expiration dates, and serial numbers, to be encoded
within a barcode. This capability enables precise tracking and traceability of products throughout the supply chain,
improving efficiency and ensuring product safety.
Example: GS1 UDI encoded in a Data Matrix code
The Data Matrix code below encodes a GS1
[Unique Device Identification](https://www.gs1.org/industries/healthcare/udi) (UDI) with multiple AIs.

The symbology identifier is `]d2`, identifying it as a GS1 Data Matrix code.
The textual data contained in this code is
`0108806388269617112302141728021310230214A3263-0121059240ARO4808C`. The ASCII group separator is a
non-printable character used to delimit variable-length AIs and represented as `` here.
The AIs encoded in this Data Matrix barcode are shown below.
| Numeric AI |Description |Length |Data |
-----------------------------------------
| 01 |GTIN |14 |08806388269617 |
| 10 |Lot number |variable |10230214A3263-01 |
| 11 |Production date |6 |230214 |
| 17 |Expiration date |6 |280213 |
| 21 |Serial number |variable |059 |
| 240 |Manufacturer-specific ID |variable |ARO4808C |
Note:
The table is sorted by numeric AIs, with smaller ones on top. In the barcode payload, the AIs do not
necessarily have to appear in this order.
To learn more about the available GS1 Application Identifiers, visit the GS1
[Application Identifiers website](https://ref.gs1.org/ai/).
## Reading AIs from Scanned Barcodes
Currently STRICH does not expose the AIs in a structured way. Instead, when a GS1 barcode is read, the appropriate
symbology identifier is included in the detection, along with the barcode payload, to help distinguish it from a
regular barcode.
We might choose to provide this feature directly in our library, given enough interest from customers.
In the meantime, we recommend you use freely available libraries such as [BarcodeParser](https://github.com/PeterBrockfeld/BarcodeParser) or
the [GS1 Digital Link Toolkit](https://github.com/gs1/GS1DigitalLinkToolkit.js) to extract the AIs from
the GS1 element string.
In the following sections, we will show how to use both tools to achieve to parse the GS1 AIs into a human-readable interpretation.
The full sample code is available in the [gs1-ai-parsing Github repository](https://github.com/pixelverse-llc/gs1-ai-parsing).
### Using the BarcodeParser Library to Extract GS1 AIs
#### Loading the BarcodeParser Library
Include the BarcodeParser library by loading it via a script tag from a CDN.
the library is not available on NPM.
Here we include the minified version via the jsDeliver CDN.
```HTML
```
### Parsing the Barcode using parseBarcode
The barcode data is parsed using the aptly named `parseBarcode` function. The function returns an object
containing an array of parsed AIs. In the code snippet below, we assemble it into newline-separated human-readable
interpretation, with each line containing the AI, the AI description, and the AI data (e.g. `10 (BATCH/LOT): 230214A3263-01`).
```JAVASCRIPT
const answer = parseBarcode(data);
let hri = '';
for (let item of answer.parsedCodeItems) {
hri += `${item.ai} (${item.dataTitle}): ${item.data}`;
hri += '\n';
}
outputElem.innerText = hri;
```
### Using the GS1 Digital Link Toolkit to Extract GS1 AIs
#### Loading the GS1DigitalLinkToolkit.js Library
Include the GS1DigitalLinkToolkit.js library by bundling it or by loading it via a script from a CDN.
The library is not available on NPM.
Here we load the latest version in the GitHub repository via the jsDeliver CDN.
```HTML
```
### Parsing the Barcode using extractFromGS1elementStrings
Instantiate the `GS1DigitalLinkToolkit` class and call the `extractFromGS1elementStrings` method, passing the
barcode data.
```JAVASCRIPT
const dlt = new GS1DigitalLinkToolkit();
const answer = dlt.extractFromGS1elementStrings(data);
```
The result of the method is a dictionary containing the AIs and the associated data.
For the sample input `0108806388269617112302141728021310230214A3263-0121059240ARO4808C`, the output
is the following dictionary:
```JSON
{
"10": "230214A3263-01",
"11":"230214",
"17":"280213",
"21":"059",
"240":"ARO4808C",
"01":"08806388269617"
}
```
# Customizing the Scanner UI
## Overview of the Barcode Scanning UI
The screenshot below illustrates a full-screen barcode scanning UI. The area supplied by the host element
is filled with the camera preview. Everything displayed on top of the camera preview is called the overlay.
The region of interest, where barcodes are detected, is delimited by a rectangular frame, called the viewfinder.

## Styling Options
The viewfinder can be styled in the following ways:
* Primary color: The color of the viewfinder rectangle and UI elements like the camera selector is set using the [primaryColor](https://docs.strich.io/reference/interfaces/OverlayConfiguration.html#primaryColor) property in the overlay configuration.
* Corner radius: The corner radius of the viewfinder rectangle and the camera selector is set using the [cornerRadius](https://docs.strich.io/reference/interfaces/OverlayConfiguration.html#cornerRadius) property in the overlay configuration.
* Targeting line visibility: visibility of the targeting line can be toggled using the [showTargetingLine](https://docs.strich.io/reference/interfaces/OverlayConfiguration.html#showTargetingLine) property.
* Targeting line active color: when a barcode is detected, the targeting line flashes briefly to a bright red. The color can be changed with the [targetingLineActiveColor](https://docs.strich.io/reference/interfaces/OverlayConfiguration.html#targetingLineActiveColor) property.
* Viewfinder border width: The border width of the viewfinder rectangle, using the [viewfinderBorderWidth](https://docs.strich.io/reference/interfaces/OverlayConfiguration.html#viewfinderBorderWidth) property.
* Viewfinder corner radius: The corner radius of the viewfinder rectangle, using the [viewfinderCornerRadius](https://docs.strich.io/reference/interfaces/OverlayConfiguration.html#viewfinderCornerRadius) property.
* Mask color: The color of the area outside of the viewfinder, using the [maskColor](https://docs.strich.io/reference/interfaces/OverlayConfiguration.html#maskColor) property.
When a barcode is detected, and highlighting of detections is enabled (using the [showDetections](https://docs.strich.io/reference/interfaces/OverlayConfiguration.html#showDetections) property), a rectangle is drawn at its location.
Barcode detections can be styled in the following ways:
* Fill color: The color used to fill the rectangle can be changed using the [detectionFillColor](https://docs.strich.io/reference/interfaces/OverlayConfiguration.html#showDetections) property.
* Border color: The color of the border surrounding the rectangle can be changed using the [detectionBorderColor](https://docs.strich.io/reference/interfaces/OverlayConfiguration.html#detectionBorderColor) property.
* Border width: The width of the border surrounding the rectangle can be changed using the [detectionBorderWidth](https://docs.strich.io/reference/interfaces/OverlayConfiguration.html#detectionBorderWidth) property.
Tip:
Colors are set using the CSS [rgb notation](https://developer.mozilla.org/en-US/docs/Web/CSS/color_value/rgb). Using hexadecimal notation (e.g. `#FF0000`) is not supported.
### Example 1: Yellow primary color with rounded corners
The following configuration styles the UI using a yellow colors, applies rounded corners and
sets the targeting line to be bright yellow in case of a detection.

Overlay configuration snippet
```JAVASCRIPT
const config = {
overlay: {
primaryColor: 'rgb(239,253,95)', // Lemon
detectionFillColor: 'rgb(249,166,2)', // Gold
detectionBorderColor: 'rgb(255,255,0)', // Bright yellow
detectionBorderWidth: 2,
targetingLineActiveColor: 'rgb(255,255,0)', // Bright yellow
cornerRadius: 4 // rounded
}
};
```
### Example 2: Using a mask to emphasize the region of interest
The following configuration styles the UI using a near-white color and emphasizes
the region of interest using a dark, semi-transparent mask color and a large border width for the viewfinder rectangle.

Overlay configuration snippet
```JAVASCRIPT
const config = {
overlay: {
primaryColor: 'rgb(236,237,237)', // Decorator's White
maskColor: 'rgba(0,0,0,0.5)',
viewfinderBorderWidth: 5,
viewfinderCornerRadius: 20
}
};
```
## Hiding or Replacing the STRICH Logo
Hiding or replacing the STRICH logo is an add-on capability available for Enterprise licenses.
License keys that include the Custom Branding capability can be used to customize the logo appearance.
Tip:
Please note that hiding or replacing the STRICH logo by other means is prohibited and constitutes a violation of the license terms.
* Hiding the logo: The built-in STRICH logo can be hidden by setting the [customLogoSrc](https://docs.strich.io/reference/interfaces/OverlayConfiguration.html#customLogoSrc) property to `null`.
* Replacing the logo: A custom logo can be used by setting [customLogoSrc](https://docs.strich.io/reference/interfaces/OverlayConfiguration.html#customLogoSrc) property to a data URL of the logo image, or an absolute URL pointing to the logo image.
Tip:
By default, custom logos are rendered at their intrinsic size (image pixels = CSS pixels). This may result in a blurred image on high-resolution screens. To prevent this from happening, use a high-resolution image and set the [customLogoSize](https://docs.strich.io/reference/interfaces/OverlayConfiguration.html#customLogoSize) property to a smaller size (e.g. supply a 200x40px image and set the size to 100x20 CSS pixels).
We recommend using a transparent SVG vector for the logo image.
# Customizing the Scanner Sound
Sometimes it is desirable to customize the sound played when a barcode is scanned, depending on business logic.
Currently STRICH itself includes only a standard "cash register" beep sound, which can be toggled on or off via the [audio](https://docs.strich.io/reference/interfaces/FeedbackConfiguration.html#audio) property of the feedback configuration.
## Use Cases for Playing Custom Sounds
Some examples of scanning scenarios where a custom sound is desirable:
* Valid/expired tickets: a positive sound is played if the QR code contains a valid ticket, a negative one if it's expired or not in the expected format.
* Format issues: a negative sound is played if the scanned barcode is in the wrong format, has an incorrect length, contains unexpected data.
* Network errors: the scanned barcode could not be looked up, play a sound to indicate that the user should try again.
As STRICH is only concerned with scanning barcodes, it cannot know if a barcode is valid in the context of its host app.
An app-specific, asynchronous operation (e.g. an HTTP request) is often required to determine its validity, which introduces an unpredictable delay.
### Playing Custom Sounds with Howler.js
The recommended approach to playing custom sounds is using a library like [Howler.js](https://howlerjs.com/) to play the sound in the [detected](https://docs.strich.io/reference/classes/BarcodeReader.html#detected) callback, and disabling the default beep by setting the [audio](https://docs.strich.io/reference/interfaces/FeedbackConfiguration.html#audio) to `false`.
Tip:
A benefit of this approach is that sound playback can be more reliable. Robust sound playback in the browser is unfortunately non-trivial, and we do not consider it a core competence of STRICH. Libraries like Howler.js are dedicated to the task of playing sound reliably across browsers.
Sample Code
In this sample, a positive sound is played if the scanned QR code contains a valid URL, and a negative sound is played otherwise. The sounds are preloaded before the scanner is started, so that they are ready to play immediately when a code is scanned.
```JAVASCRIPT
// import Howler.js for playing sounds
import { Howl } from 'https://cdn.jsdelivr.net/npm/howler@2.2.4/+esm';
// prepare Howler.js sounds
const sounds = {
positive: new Howl({
src: ['positive.mp3'],
preload: true
}),
negative: new Howl({
src: ['negative.mp3'],
preload: true
})
}
/** Play a positive sound if QR Code contains a valid URL, negative otherwise */
function processDetection(detection) {
try {
const parsedUrl = new URL(detection.data);
console.log(`QR code contains URL: ${parsedUrl}`)
sounds.positive.play();
} catch (e) {
// TypeError thrown if URL is not OK
sounds.negative.play();
}
}
try {
await StrichSDK.initialize(``);
const cfg = {
selector: '.barcode-reader',
engine: {
symbologies: ['qr'],
},
feedback: {
audio: false // disable STRICH's default beep
}
};
let barcodeReader = new BarcodeReader(cfg);
barcodeReader.detected = (detections) => processDetection(detections[0]);
await barcodeReader.initialize();
await barcodeReader.start();
} catch (e) {
window.alert(e.message);
}
```
# Non-Modular Build Flavor
STRICH is distributed as an ES6 module, to be imported into your app via an
[import statement](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Modules#importing_features_into_your_script).
If you are running in an environment where you can not use ES6 modules, you can alternatively include the library via
a `
```
The non-modular builds registers the classes `StrichSDK`, `BarcodeReader` and `SdkError` under a global
`strich` object, so instead of accessing importing `StrichSDK` and accessing it directly, you
need to access it through the global:
```JAVASCRIPT
// non-modular build, objects published under global 'strich'
await strich.StrichSDK.initialize(...)
```
# API Reference
The API Reference is located [here](https://docs.strich.io/reference/index.html).
# Sample Code
We aim to provide consistent, clean and idiomatic sample code for the most popular frontend frameworks. If you have
feedback regarding our sample code (we are by no means expert in every framework!), feel free to send us your comments
to [hello@pixelverse.ch](mailto:hello@pixelverse.ch) or open a pull request in the Github repository.
## Structure of Sample App
Most of our code samples show the following three usage scenarios:
Single Scan – Scan a single barcode and return to the previous screen. Illustrates basic SDK usage such as SDK and BarcodeReader initialization and lifecycle management.
[Video](images/sample_single_scan.mp4)
Repeated Scans – Scan barcodes repeatedly with an intermediate user action between scans until an application-specific criterion is met. Illustrates using `start()` and `stop()` to control the `BarcodeReader`,
[Video](images/sample_repeated_scans.mp4)
Continuous Scans - Scan barcodes continuously until an application-specific criterion is met (e.g. all N barcodes on a label are scanned).
[Video](images/sample_continuous_scans.mp4)
## Code Samples by Framework
Vanilla JS
: An example of how to use STRICH without any web framework at all, using just plain ES6.
[Github repository](https://github.com/pixelverse-llc/strich-javascript-sample)
Angular
: Sample integration into an Angular web app. Uses standalone components.
[Github repository](https://github.com/pixelverse-llc/strich-angular-sample)
SvelteKit
: Sample integration into a SvelteKit web app. Uses SvelteKit 2.x.
[Github repository](https://github.com/pixelverse-llc/strich-sveltekit-sample)
React
: Sample code showing how to integrate STRICH into a React project. Uses React functional components (React 16.8+).
[Github repository](https://github.com/pixelverse-llc/strich-react-sample)
Vue 3
: Sample code showing how to integrate STRICH into a Vue 3.x project.
[Github repository](https://github.com/pixelverse-llc/strich-vue3-sample)
iOS (Swift/Swift UI)
: Sample code for integrating a web app that uses STRICH into a native iOS app using a WebView.
[Github repository](https://github.com/pixelverse-llc/strich-ios-sample)
Vaadin Flow (WIP)
: Sample code showing how to integrate STRICH into a Vaadin Flow project.
[Github repository](https://github.com/pixelverse-llc/strich-vaadin-sample)
Vue 2 (deprecated)
: Sample code showing how to integrate STRICH into a Vue 2.x project. This sample is no longer maintained.
[Github repository](https://github.com/pixelverse-llc/strich-vue2-sample)
## Contributing
Are you interested in contributing to our samples or even providing a sample of your own that we can link to?
Drop us a line at [hello@pixelverse.ch](mailto:hello@pixelverse.ch). Here's what we value in submissions:
Clean
: Sample code should not contain extra files or dependencies that are not required to run the sample or dilute the sample's purpose.
Minimal
: Sample code should not contain business logic or styling and focus solely on idiomatic integration into the target environment.
Self-contained
: Sample code should not require extra dependencies apart from the target environment itself, e.g. an Angular sample
should depend only on Angular, and not on additional libraries such as Angular Material.
Documented
: A short README containing a description of the sample and pointing out the salient bits is a must, as well as specifying a license for the code.
## Licensing
Sample code is provided as-is under the [CC0 license](https://creativecommons.org/publicdomain/zero/1.0/deed.en).
The code is for illustrative purposes only and may not be suitable for production.
## External Contributions
Note:
The following code samples were written by outside contributors who are not affiliated with
Pixelverse GmbH. We are not responsible for their contents, and can not be held liable for any damages resulting in their use.
Blazor
: Sample code showing how to integrate STRICH into a Blazor project. [Github repository](https://github.com/skjwang/Blazor-Strich)
: Contributed by: Shaokang Wang and Pierre Mertz, Infinera
# Tutorials
A collection of more in-depth, hands-on advice on building scanning apps with STRICH.
# Creating a JavaScript Barcode Scanning App From Scratch
In this tutorial, we will build an app that uses barcode scanning from scratch. The aim
is not to build a production-ready, real-world app, but instead illustrate how common scanning workflows
can be implemented using the SDK's capabilities and plain JavaScript/HTML/CSS.
The app is deployed under [https://pixelverse-llc.github.io/inventory-tracking-app/](https://pixelverse-llc.github.io/inventory-tracking-app/)
and full source is available on [GitHub](https://github.com/pixelverse-llc/inventory-tracking-app).
## Inventory Tracking
The app that we'll build will do inventory tracking. The user signs
into to the app and can borrow items (e.g. a laptop) from
a storage locker, and return them later. Tracking of physical items
is a task that is often done using barcode scanning.
## Tech Stack
In frontend development, there are always a lot of different ways
to achieve the same thing for any given task. For this tutorial, we have chosen a
deliberately minimal approach that should be easily adaptable
to your framework of choice.
### Vanilla JS/HTML/CSS (No Framework)
We are going to implement the app without using any frontend frameworks
or libraries, relying only on the baseline capabilities
available in modern web browsers – JavaScript, HTML and CSS.
This might seem like an odd choice, but STRICH is framework-agnostic and
aims to work well in any web environment.
We encourage you to try building an app with just the core Web primitives – it's a refreshing experience.
### No Styling
We will not use any styling beyond basic layout and making the buttons
touch-friendly. We will make it functional and have acceptable UX.
The app will not look beautiful.
Tip:
In a real-world app, you would likely use a CSS framework like
Tailwind CSS, Bootstrap or UIkit.
Feel free to add some style to the app if you want!
### No Backend - localStorage Only
To keep things simple, the app will not use a backend and instead use [localStorage](https://developer.mozilla.org/en-US/docs/Web/API/Window/localStorage)
to store the currently logged-in user and borrowed inventory.
Tip:
In a real-world app, users would be authenticated using a scheme like OAuth
and the state of the inventory would be stored in a database such as PostgreSQL.
### No Build Steps, No NPM
No build steps like bundling and minification involved. The HTML, JS and CSS that we create can be opened
as-is.
Tip:
In a real-world app you would use a dependency manager like NPM and these steps would be performed by tools
like Vite, Webpack and others.
## How the App uses Barcode Scanning
The app will use barcode scanning for two purposes: user identification and item scanning.
### Scanning Badge QR Code
The app will require an user to scan a QR Code printed on a hypothetical
badge before items can be borrowed. We'll assume the QR Code contains
a string of digits, like the one below.

### Scanning Inventory Items
Items are borrowed and returned by scanning the 1D barcode printed on them.
We assume that items are labelled using Code 128, Code 39 or EAN/UPC
product barcodes.
Here's a screen recording of a user scanning three PlayStation games in
quick succession:
[Video](images/inventory_scanning.mp4)
## Project Setup
### Git Repository
We will store the app in a Git repository and use GitHub Pages to deploy
it under a publicly reachable, HTTPS-enabled URL. We'll assume that you
are already familiar with Git, the de-facto standard for version control.
If you haven't used Git before, we recommend GitHub's
[Getting Started](https://docs.github.com/en/get-started/learning-to-code/getting-started-with-git)
documentation.
Tip:
We decided to use GitHub Pages because it's popular and free, and if you're reading this,
you probably already have a GitHub account. There is no reason why you couldn't also use something else, e.g.
Cloudflare Pages, Netlify, or self-hosting.
GitHub Pages automatically provides your site with a valid SSL certificate, which is
required for camera access in the browser outside of localhost, but other services do this as well.
Create a new Git repository on GitHub.
Then, in your repository settings, make sure GitHub Pages is enabled, and choose `main` as the branch
and `/ (root)` as the repository path from where to deploy your web app. Your settings should
look similar to the screenshot below.

Clone your new Git repository to your local machine using the command-line, or a Git client
of your choice, using the URL shown in GitHub. If you want to work directly with the final
repository, run the following command:
```SHELL
git clone git@github.com:pixelverse-llc/inventory-tracking-app.git
```
### Directory Structure
Our app consists of the following HTML, CSS and JavaScript files.
| Path |Contents |
------------------
| /index.html |Login and inventory screen |
| /scan.html |Item scanning screen |
| /css/styles.css |Global stylesheet (shared) |
| /lib/authentication.js |User authentication module (shared) |
| /lib/inventory.js |Inventory management module (shared) |
| /img/badge_qr.webp |Badge image with QR Code |
### Hello, World on GitHub Pages
Get started by adding an `index.html` file to the folder where you checked out your repository.
Edit it and add a minimal Hello, world! document:
```HTML
Hello, world!
```
Add the `index.html` file to Git, commit it and push it to the `main` branch.
```SHELL
git add index.html
git commit -m "initial commit" index.html
git push
```
If everything is set up properly, you should be able to access your new GitHub Pages site via the
URL https://USERNAME.github.io/REPOSITORY/ (URL will also be shown in the settings) after a short
wait, and be greeted with your shiny new app:

Not terribly impressive, but you have a working GitHub Pages setup now! Feel free to play around with
it until you're ready for the next step.
### Optional: Run a Web Server Locally
For development purposes, it is useful to run a web server that serves the contents of the repository
on your local machine. If you have Python installed (it comes pre-installed on macOS), you can launch
a simple HTTP server with the following command:
```SHELL
python3 -m http.server
```
You can then open your web app by opening the URL http://localhost:8000 in your web browser.
Tip:
Running a local web server is easy, but setting up a valid SSL certificate is usually
not straightforward. Outside of localhost, browsers require an HTTPS connection for accessing
the smartphone's camera. Tools like [ngrok](https://ngrok.com) and
[alternatives](https://github.com/anderspitman/awesome-tunneling) make this easy, and we
recommend using them for development and testing on smartphones.
### Obtaining a STRICH SDK License Key
Next, you will need to get a license key for the STRICH SDK.
Create an account in the [Customer Portal](https://portal.strich.io/register/) and start the
free trial. A credit card or PayPal will be required, but it will not be charged during the trial.
Then create a license key, and when asked for a URL, use your GitHub Pages URL.

## Building the Login Screen
Now that we have an SDK license key, we can start scanning some codes!
The first thing our users will see is a login screen. It will consist of a header, an image of a badge
with a QR Code printed on it, and a button to scan the QR Code:

Adding a button to start scanning instead of starting it directly
when the page has loaded is recommended, because it allows you to inform the user that access to the camera will be required, and
for what purpose. Initially the user will have to grant permission to access the camera to the app, and if the reason
is not clear and the prompt appears seemingly out of the blue, it will likely be denied.
### Login Screen HTML
In the HTML for the login screen, we set a
[mobile viewport](https://developer.mozilla.org/en-US/docs/Web/HTML/Guides/Viewport_meta_element#viewport_basics),
include a CSS stylesheet (we'll create it later) and display the screen contents in a centered Flex container
which will contain the header, image and button.
Update your `index.html` to match the snippet below.
```HTML
Inventory Tracking App
Scan Badge
```
### Login Screen Stylesheet (CSS)
To style our app, we will use a CSS stylesheet and store it under `css/styles.css`.
The CSS does a subset of what most [CSS Resets](https://en.wikipedia.org/wiki/Reset_style_sheet) do:
use the more intuitive `border-box` box-sizing model, remove margins, and make images responsive.
We also make buttons more touch-friendly by making them full-width and increasing their height
to 48px, the recommended size for mobile-friendly buttons.
The content of our screens will live inside a `main` element, which we will
center in the screen using Flex layout.
```CSS
/* see: https://css-tricks.com/box-sizing/#aa-universal-box-sizing */
*, *:before, *:after {
box-sizing: border-box;
}
/* remove body margin */
body {
margin: 0;
}
/* responsive images */
.img-responsive {
max-width: 100%;
height: auto;
}
/* make buttons wide and touch-friendly */
button {
min-height: 48px;
width: 100%;
}
/* centered content container */
main {
max-width: 640px;
padding: 20px;
display: flex;
flex-direction: column;
height: 100dvh;
justify-content: center;
margin: auto;
}
```
For the full stylesheet, please check out the
[styles.css](https://github.com/pixelverse-llc/inventory-tracking-app/blob/main/css/styles.css)
in the GitHub repository.
### Login Screen Logic (JavaScript)
To fill the screen with some life, we need to add some JavaScript that:
* [Imports](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/import) the latest version of the STRICH SDK as an ES6 module from the jsDeliver CDN.
* Checks if the device has a camera using the [hasCameraDevice](https://docs.strich.io/reference/classes/StrichSDK.html#hasCameraDevice) method, and if not, displays an error message to the user.
* Initializes the STRICH SDK using the license key we obtained previously.
* Enables the SCAN QR CODE button, and makes it open a [PopupScanner](https://docs.strich.io/the-popup-scanner.html) instance configured to read QR Codes. We don't do anything with scanned code yet.
Tip:
For simplicity, the JavaScript is included directly in the `index.html` file. In a real-world app, you
might prefer keeping it in a separate file (e.g. `index.js`).
Populate the empty script tag in `index.html` with the following JavaScript, and substitute the
placeholder with your license key.
```JAVASCRIPT
import { StrichSDK, PopupScanner } from "https://cdn.jsdelivr.net/npm/@pixelverse/strichjs-sdk@latest";
/**
* Attempt to scan a QR Code using the PopupScanner.
*
* See: https://docs.strich.io/the-popup-scanner.html
*/
async function scanQRCode() {
const qrCodes = await PopupScanner.scan({
symbologies: ['qr'],
labels: {
title: 'Scan Badge'
}
});
// if a QR Code was scanned, use its encoded value as the user, and update the UI
if (qrCodes) {
// log in...
}
}
async function updateUI() {
// set up the QR Code login screen, but check if the browser has access to a camera
const hasCamera = await StrichSDK.hasCameraDevice();
if (hasCamera) {
const scanQrButton = document.getElementById('scan-qr-button');
try {
const licenseKey = '';
await StrichSDK.initialize(licenseKey);
scanQrButton.disabled = false;
scanQrButton.onclick = scanQRCode;
} catch (err) { // handle SDK error: https://docs.strich.io/reference/classes/SdkError.html
alert(`Failed to initialize STRICH SDK, your license key may have expired: ${err.message}`);
}
} else {
alert(`Sorry, you need a camera to scan barcodes`);
}
}
// initial UI update
updateUI();
```
Committing and pushing the updated `index.html` file will cause a new version of your GitHub Pages site
to be deployed.

You should now be able to see your new login page and trigger a QR Code scan. Try it out!
### The Authentication Module
Our app uses the data in the badge QR Code to log the user into the app.
Since we do not have a real backend against which we could authenticate the user, we will simply treat
the data encoded in the QR Code as the username, and store it in
[localStorage](https://developer.mozilla.org/en-US/docs/Web/API/Window/localStorage).
We will encapsulate the login functionality in an
[ES6 module](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Modules) that can be shared
between all pages that require access to the login information.
lib/authentication.js: Utility functions for storing the currently logged-in user
The `lib/authentication.js` module provides access to the name of the logged-in user, and functions
for setting the logged-in user, and logging out.
```JAVASCRIPT
// the key to use to store a logged-in user in localStorage
const usernameKey = 'username';
// the currently logged-in username, or null, if the user is logged out
export function getUsername() {
return localStorage.getItem(usernameKey);
}
export function logOut() {
localStorage.removeItem(usernameKey);
}
export function logIn(username) {
localStorage.setItem(usernameKey, username);
}
```
To load the module in the login screen, import it along-side the SDK.
```JAVASCRIPT
import { StrichSDK, PopupScanner } from "https://cdn.jsdelivr.net/npm/@pixelverse/strichjs-sdk@latest";
import { logIn } from './lib/authentication.js';
async function scanQRCode() {
// ... PopupScanner ...
// if a QR Code was scanned, use its value as the user, and update the UI
if (qrCodes) {
logIn(qrCodes[0].data);
await updateUI();
}
}
```
## Building the Inventory Screen
After the user has logged in, we want to display the main screen: the list of borrowed inventory,
and buttons for the primary actions.
The screen consists of a title, a list of items (barcode and count) and buttons to borrow or lend items.
At the bottom, the username of the logged-in user is displayed, along with a button
for logging out.

### Inventory Screen Layout (HTML)
Add the following snippet as a child of the `` element. The view is initially
hidden, and will be shown only if the user is logged in.
```HTML
My Items
There are no items in your inventory yet.
– ()
Logged in as:
```
The list items will be generated dynamically. We use an HTML
[template](https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Elements/template)
element for the list item template.
### Inventory Screen Logic (JavaScript)
To switch between the logged-out and logged-in views, we have to extend the previous `updateUI()` function.
Depending on the authentication state, it will either make the `logged-in` view or the `logged-out` view visible.
If the user is logged in, the borrowed inventory is loaded from a module that exposes a `loadItems()` function.
The returned items are used to populate the list using the dynamically instantiated item template.
If the borrowed inventory is empty, a placeholder is made visible instead.
We hook up the Borrow Items and Return Items buttons and make them navigate to a scan screen, passing a query parameter
containing the mode (`borrow` or `return`). We disable the Return Items button if the inventory is empty.
At the bottom, we display the logged-in username and a button that logs the user out, clearing the inventory.
```JAVASCRIPT
import { loadItems, clearItems } from './lib/inventory.js';
/**
* Update the UI depending on the current application state (authenticated: yes/no, inventory items)
*/
async function updateUI() {
const isLoggedIn = getUsername() !== null;
// show logged-in or logged-out view depending on authentication state
document.getElementById('logged-out').style.display = isLoggedIn ? 'none' : 'block';
document.getElementById('logged-in').style.display = isLoggedIn ? 'block' : 'none';
if (isLoggedIn) {
// display stored items in a list
const items = loadItems();
// show placeholder if list is empty
document.getElementById('no-items-placeholder').hidden = items.length > 0;
// populate list from item template
const listElement = document.getElementById('item-list');
document.getElementById('item-list').hidden = items.length === 0;
for (const item of items) {
const itemElement = document.getElementById('item-template').content.cloneNode(true);
itemElement.querySelector('.item-barcode').innerHTML = item.barcode;
itemElement.querySelector('.item-count').innerHTML = `${item.count}x`;
listElement.appendChild(itemElement);
}
// if we don't have any items, disable the return items button
if (items.length === 0) {
document.getElementById('return-items').disabled = true;
}
// borrow items/return items buttons navigate to scan page
document.getElementById('borrow-items').onclick = () => document.location.href = 'scan.html?mode=borrow';
document.getElementById('return-items').onclick = () => document.location.href = 'scan.html?mode=return';
// show logged-in user and logout button, clears inventory
document.getElementById('username').innerHTML = getUsername();
document.getElementById('log-out').onclick = () => {
logOut();
clearItems();
updateUI();
};
} else {
// logged out logic (see previous sections)
}
}
```
Tip:
The DOM manipulation might feel convoluted if you're used to frontend frameworks that allow you to
bind elements directly to data and control logic.
Check out the [index.html](https://github.com/pixelverse-llc/inventory-tracking-app/blob/main/index.html)
file in the GitHub repository for the full code.
### The Inventory Module
Where is `loadItems()` coming from? Similar to the authentication module, we create a module that
encapsulates all interactions with the inventory:
* Getting the list of borrowed items
* Adding/removing an item
* Clearing the list items
Since we are not using a backend, we will again use localStorage to store the borrowed items
locally. For every item, a barcode and count are stored.
Adding an item that already exists increments the existing item's count. Removing an item
decrements the count. If the count reaches 0, the item is removed from the list.
We use JSON to convert the array of items to a string and vice versa, suitable for storing
it in localStorage.
```JAVASCRIPT
// Expose a list of items from localStorage
const itemsKey = 'items';
export function clearItems() {
localStorage.removeItem(itemsKey);
}
export function loadItems() {
const itemsStr = localStorage.getItem(itemsKey);
return itemsStr === null ? [] : JSON.parse(itemsStr);
}
export function storeItems(items) {
localStorage.setItem(itemsKey, JSON.stringify(items));
}
export function addItem(item) {
const myItems = loadItems();
const existingItemIdx = myItems.findIndex(it => it.barcode === item.barcode);
if (existingItemIdx === -1) {
myItems.push(item);
} else {
myItems[existingItemIdx].count += item.count;
}
storeItems(myItems);
}
export function removeItem(item) {
const myItems = loadItems();
const existingItemIdx = myItems.findIndex(it => it.barcode === item.barcode);
if (existingItemIdx === -1) {
myItems.splice(existingItemIdx, 1);
} else {
myItems[existingItemIdx].count -= item.count;
if (myItems[existingItemIdx].count <= 0) {
myItems.splice(existingItemIdx, 1);
}
}
storeItems(myItems);
}
```
Save the module's code in a file called `lib/inventory.js`.
## Building the Scanning Screen
We've built the main screen, but our inventory is empty.
The scanning screen is where we finally scan items and add them to the inventory.
The scanning screen will have two modes: borrowing items and returning items.
Depending on the mode, the scanning screen will display a different title, and interact with the
inventory module differently.
The barcode reader will be displayed on top, a label showing the number of items scanned and a Finish Scanning
button below.

The screen will allow scanning multiple items in one go, making the
[Popup Scanner](https://docs.strich.io/the-popup-scanner.html) less suitable. Instead, we will
use [BarcodeReader](https://docs.strich.io/getting-started.html#choosing-the-integration), a more customizable
integration. Check out the [Getting Started Guide](https://docs.strich.io/getting-started.html#choosing-the-integration)
for more information about the different ways to integrate STRICH into your app.
### Scanning Screen Layout (HTML)
Copy the `index.html` file and rename it to `scan.html`. Replace the `` element with the snippet below.
```HTML
Return Items
Return items to the storage by scanning their barcodes.
Borrow Items
Take items from the storage by scanning their barcodes.
No items scanned yet
```
### Scanning Screen Logic (JavaScript)
The mode is passed to the screen via a query parameter, e.g. `scan.html?mode=borrow`.
Scanned items are accumulated in the `onItemScanned()` function and stored in the `scannedItems` variable.
Compared to scanning the badge QR Code, setting up the barcode scanning code here is a bit more complex.
A [BarcodeReader](https://docs.strich.io/reference/classes/BarcodeReader.html) is created by invoking its
constructor with a [Configuration](https://docs.strich.io/reference/interfaces/Configuration.html) object.
The configuration contains the list of barcode types (symbologies) to detect, as well as a parameter that
suppresses duplicate scans, and most importantly, the [host element](https://docs.strich.io/reference/interfaces/Configuration.html#selector).
The host element is the HTML element that will contain the visible elements of the BarcodeReader. It needs
to have a fixed size and use [relative positioning](https://developer.mozilla.org/en-US/docs/Web/CSS/position#relative).
When a barcode is detected, the BarcodeReader is instructed will invoke the `onItemScanned()` callback,
which accumulates the item in the `scannedItems` variable.
After we've configured the BarcodeReader, we initialize and start barcode detection.
When scanning is finished, we [destroy](https://docs.strich.io/reference/classes/BarcodeReader.html#destroy) the
BarcodeReader and navigate back to the main screen.
Add the following JavaScript snippet to the `