Skip to content

How to scan QR codes and barcodes in JavaScript

To scan QR codes in JavaScript, open the camera with getUserMedia, show it in a video element, and pass frames to a decoder. Use the built-in BarcodeDetector API where the browser has it, and a ZXing build such as zxing-wasm everywhere else. The same decoders read uploaded images.

Two ways to decode: BarcodeDetector or ZXing

BarcodeDetector is part of the Shape Detection API. It is built into Chromium-based browsers on Android, macOS and ChromeOS, but not Firefox or desktop Safari, and not Chrome on Windows or Linux at the time of writing. It costs nothing to load, so use it when it exists, and feature-check first.

ZXing is the long-standing open-source barcode library. zxing-wasm compiles the C++ version, zxing-cpp, to WebAssembly and runs in every modern browser. It reads QR codes, Data Matrix, Aztec, PDF417, EAN, UPC, Code 128 and more, several per image. NeoReader's own scanner uses zxing-wasm. @zxing/browser is the older pure JavaScript port; it still works, but the underlying library is in maintenance mode.

  • BarcodeDetector: no download, fast, limited browser support.
  • zxing-wasm: works everywhere, one WebAssembly file to load, wide format support.
  • @zxing/browser: pure JavaScript, has helpers for the camera, slower on hard images.

Read a QR code from the camera with BarcodeDetector

The camera needs a secure context, so the page must be served over HTTPS (localhost is allowed during development), and the user must grant permission. Add a video element with the playsinline and muted attributes so iPhones play it inline.

  1. Check support: if (!('BarcodeDetector' in window)) fall back to zxing-wasm.
  2. Create the detector: const detector = new BarcodeDetector({ formats: ['qr_code'] });
  3. Open the rear camera: const stream = await navigator.mediaDevices.getUserMedia({ video: { facingMode: 'environment' } });
  4. Show it: video.srcObject = stream; await video.play();
  5. Detect in a loop: const codes = await detector.detect(video); if (codes.length) handle(codes[0].rawValue);
  6. Run the loop with requestAnimationFrame or a timer every 150 to 250 ms, and skip a frame if the previous detect has not finished.
  7. Stop the camera when done: stream.getTracks().forEach((track) => track.stop());

Read QR codes and barcodes with zxing-wasm

Install it with npm i zxing-wasm. The reader-only build is smaller if you do not need to generate codes.

import { readBarcodes } from 'zxing-wasm/reader';

const results = await readBarcodes(file, { formats: ['QRCode'], tryHarder: true });

console.log(results[0]?.text);

readBarcodes accepts a File or Blob, an ArrayBuffer or Uint8Array, or ImageData, and returns an array of results with the text, the format and the corner positions. Leave out formats to read every supported type, or list several, such as ['QRCode', 'EAN13', 'Code128'].

For the camera, draw the current video frame onto a canvas and pass its pixels: ctx.drawImage(video, 0, 0, canvas.width, canvas.height); then const frame = ctx.getImageData(0, 0, canvas.width, canvas.height); and call readBarcodes(frame, options). Create the context with getContext('2d', { willReadFrequently: true }) so repeated reads stay fast.

By default, zxing-wasm fetches its .wasm file from the jsDelivr CDN. To serve it from your own domain, call prepareZXingModule with a locateFile override before the first read, as its README shows. That keeps the page working offline and avoids a third-party request.

Decode a QR code from an uploaded image

Scanning a screenshot or photo needs no camera permission. Add an input of type file with accept set to image/*, and listen for pastes too, so people can press Ctrl+V or Cmd+V with a screenshot on the clipboard.

With zxing-wasm, pass the File straight to readBarcodes. With BarcodeDetector, turn it into a bitmap first: const bitmap = await createImageBitmap(file); const codes = await detector.detect(bitmap);

Large phone photos can be slow. Scaling the image down to around 1,500 to 2,000 pixels on its longest side before decoding usually keeps accuracy and cuts the time a lot.

Using @zxing/browser

If you want the ZXing port's built-in camera handling, install @zxing/browser and create a reader: const reader = new BrowserMultiFormatReader();

Its decodeFromVideoDevice method takes a camera device ID (undefined picks the default), a video element and a callback that receives each result; call result.getText() for the content. It resolves to a controls object, and controls.stop() releases the camera.

On Android, the ZXing name also covers native libraries such as zxing-android-embedded, and for Angular there is ngx-scanner. A popular HTML5 wrapper named html5-qrcode adds a ready-made scanning UI on top of the JavaScript port.

Tips that make a JavaScript QR code reader feel fast

  • Ask for a sensible resolution, such as width: { ideal: 1280 }. Full 4K frames are slow to decode and rarely help.
  • Crop to the centre of the frame, where the user aims, before decoding.
  • Run zxing-wasm in a Web Worker so decoding never blocks scrolling or the UI.
  • Ignore the same value for a second or two after a hit, so one code does not fire twenty times.
  • Show the decoded link before opening it. QR phishing is common, so never redirect automatically.
  • Handle errors from getUserMedia: NotAllowedError means permission was refused, NotFoundError means there is no camera. Offer image upload as the fallback.

Generating barcodes in JavaScript

zxing-wasm can also write codes: writeBarcode('https://example.com', { format: 'QRCode' }) resolves to a result with an SVG string and a PNG image. For QR codes alone, the qrcode package on npm is widely used in Node and the browser, and JsBarcode draws line barcodes such as Code 128 and EAN-13 into SVG or canvas. Our guide to generating QR codes in code covers Python, PHP, Java, Node and .NET.

Read next

FAQ

Questions

Which browsers support BarcodeDetector?

Chromium-based browsers on Android, macOS and ChromeOS. Firefox and desktop Safari do not, and neither does Chrome on Windows or Linux, so always feature-check and fall back to a ZXing build.

What is the best npm package for a QR code scanner?

zxing-wasm reads QR codes and most barcodes in every modern browser and is what NeoReader uses. @zxing/browser is a pure JavaScript alternative with camera helpers.

Why does my HTML5 QR code scanner not open the camera?

The page must be on HTTPS or localhost, the user must allow camera access, and on iPhone the video needs the playsinline attribute.

Can JavaScript read a QR code from an image file?

Yes. Pass the File to zxing-wasm's readBarcodes, or create an ImageBitmap and pass it to BarcodeDetector. No camera permission is needed.

Does scanning in JavaScript send images to a server?

Not with these libraries. BarcodeDetector and zxing-wasm decode in the browser, so the image stays on the device unless your own code uploads it.