React

TypeScriptstable

Use the QRGenScanner and QRGenBarcode React components: props, event callbacks, the imperative ref handle, and server-side rendering notes.

Edit on GitHub

qrgen-sdk/react provides two components that wrap the web components: QRGenScanner for camera scanning and QRGenBarcode for rendering barcodes. They work with React 17, 18 and 19.

Install

npm install qrgen-sdk

Note: Until the first npm release is published, install the latest build with npm install https://rockyljewell.github.io/QR-GEN/downloads/qrgen-sdk.tgz.

Scan

import { useState } from "react";
import { QRGenScanner, type Barcode } from "qrgen-sdk/react";

export function CheckoutScanner() {
  const [items, setItems] = useState<Barcode[]>([]);

  return (
    <section>
      <QRGenScanner
        symbologies={["ean13", "upca", "upce", "code128"]}
        duplicateFilter={3000}
        style={{ height: 420 }}
        onScan={(barcodes) => setItems((prev) => [...barcodes, ...prev])}
        onError={(error) => console.warn(error.code, error.message)}
      />
      <ul>
        {items.map((b) => (
          <li key={`${b.timestamp}-${b.data}`}>
            {b.symbologyName}: {b.data}
          </li>
        ))}
      </ul>
    </section>
  );
}

The camera starts when the component mounts and stops when it unmounts. Camera access requires HTTPS or http://localhost.

QRGenScanner props

Prop Type Default Description
symbologies string[] | string all Ids, aliases or groups.
mode "single" | "continuous" | "batch" "continuous" Scan mode.
duplicateFilter number 1000 (-1 in batch) Milliseconds; 0 every frame, -1 once per session.
beep boolean true Tone on scan.
vibrate boolean true Vibration on scan.
camera "back" | "front" | string "back" Facing mode or deviceId. Changing it restarts the camera.
torch boolean false Flashlight, where supported.
viewfinder "frame" | "line" | "none" depends on symbologies Viewfinder style.
scanArea { x, y, width, height } from the viewfinder Normalized decode region.
maxResults number 1 (20 in batch) Codes per frame.
autostart boolean true Start on mount. When false, a Start scanning screen is shown.
controls "default" | "none" "default" "none" hides the built-in buttons.
accent string #5cc9d6 Accent color (hex).
hint string mode-specific Instruction text; "" hides it.
toast boolean true Success toast.
tryHarder boolean false (single/continuous), true (batch) More CPU per frame for difficult codes.
resolution "sd" | "hd" | "fhd" | "4k" "hd" Camera resolution.
className string Class on the <qrgen-scanner> element.
style CSSProperties Inline style. Give the scanner a height.

Callbacks

Prop Signature When
onScan (barcodes: Barcode[], barcode: Barcode) => void New codes passed the duplicate filter. barcode is the first one.
onTrack (update: { tracked, added, removed }) => void Batch mode, every decoded frame.
onSelect (detail: { barcode: TrackedBarcode | null, selected: boolean, selection: TrackedBarcode[] }) => void Batch results selection changed.
onError (error: { code: QRGenErrorCode, message: string }) => void Starting failed. See error codes.
onReady (detail: { engine: "worker" | "main" }) => void Scanning started.
onStateChange (state: ScannerState) => void idle, starting, scanning, paused, stopped, error.

Callbacks always see the latest props, so inline arrow functions don’t cause re-subscriptions.

Ref handle

Pass a ref to control the scanner imperatively:

import { useRef, useState } from "react";
import { QRGenScanner, type QRGenScannerHandle } from "qrgen-sdk/react";

export function LoginScanner({ onToken }: { onToken: (token: string) => void }) {
  const scanner = useRef<QRGenScannerHandle>(null);
  const [torch, setTorch] = useState(false);

  return (
    <div>
      <QRGenScanner
        ref={scanner}
        symbologies="qr"
        mode="single"
        controls="none"
        style={{ height: 360 }}
        onScan={(_, barcode) => onToken(barcode.data)}
      />
      <button onClick={() => scanner.current?.resume()}>Scan again</button>
      <button onClick={async () => setTorch((await scanner.current?.toggleTorch()) ?? false)}>Torch {torch ? "off" : "on"}</button>
      <button onClick={() => scanner.current?.switchCamera()}>Switch camera</button>
    </div>
  );
}
Member Returns
start() Promise<void>
stop() void
pause() void
resume() void
setTorch(on) Promise<boolean>
toggleTorch() Promise<boolean>
switchCamera() Promise<void>
setZoom(zoom) Promise<boolean>
clearSelection() void
selectBarcode(id, selected?) boolean
element The underlying QRGenScannerElement, or null before mount

Use element to read selection, lastResult or the underlying scanner for frame events and stats:

scanner.current?.element?.clearSelection();
console.log(scanner.current?.element?.scanner?.stats);

Batch mode

import { useState } from "react";
import { QRGenScanner, type TrackedBarcode } from "qrgen-sdk/react";

export function ShelfScanner() {
  const [inView, setInView] = useState<TrackedBarcode[]>([]);
  const [selected, setSelected] = useState<TrackedBarcode[]>([]);

  return (
    <>
      <QRGenScanner
        mode="batch"
        symbologies="retail"
        style={{ height: "70vh" }}
        onTrack={({ tracked }) => setInView(tracked)}
        onSelect={({ selection }) => setSelected(selection)}
      />
      <p>
        {inView.length} in view, {selected.length} selected
      </p>
    </>
  );
}

onTrack fires on every decoded frame (up to 24 times per second). If you render heavy UI from it, throttle the state updates. See Batch scanning.

QRGenBarcode

import { QRGenBarcode } from "qrgen-sdk/react";

export function Ticket({ id }: { id: string }) {
  return <QRGenBarcode value={`https://example.com/t/${id}`} symbology="qr" ecLevel="Q" style={{ width: 180 }} />;
}

Props: value (required), symbology, scale, ecLevel, foreground, background, hrt, margin, gs1, rotate, alt, className, style. They match the <qrgen-barcode> attributes.

Server-side rendering

Both components are safe to import on the server. During SSR they render an empty <qrgen-scanner> or <qrgen-barcode> tag with attributes; the custom elements register and start in the browser. Nothing touches window or the camera at import time.

The components use hooks, so in frameworks with React Server Components (the Next.js App Router, for example) render them from a Client Component, a file that starts with "use client". See Next.js. If you call BarcodeScanner or scanImage directly, do it in an effect or event handler, never during render.

Without the wrapper

You can also use the element directly in JSX. Import qrgen-sdk/elements once to register it:

import { useEffect, useRef } from "react";
import "qrgen-sdk/elements";
import type { Barcode } from "qrgen-sdk";

export function RawScanner() {
  const ref = useRef<HTMLElement>(null);

  useEffect(() => {
    const el = ref.current;
    if (!el) return;
    const onScan = (e: Event) => console.log((e as CustomEvent<{ barcode: Barcode }>).detail.barcode.data);
    el.addEventListener("scan", onScan);
    return () => el.removeEventListener("scan", onScan);
  }, []);

  return <qrgen-scanner ref={ref} symbologies="qr" style={{ height: 400 }} />;
}

Importing qrgen-sdk/react anywhere in your app adds the JSX type declarations for <qrgen-scanner> and <qrgen-barcode>. Without it, declare them in your own .d.ts file.

Esc

Share this page

React · Docs | QRGen

Star QRGen on GitHub Add a "Powered by QRGen" badge