Add this component

npx zentauri-ui add qr-scanner

Accessibility notes

Camera access via getUserMedia requires a secure context (HTTPS or localhost) and a user gesture. The component shows fallback UI when camera is unavailable.

Dependency notes

Requires the jsqr npm package. Install it alongside Zentauri UI: pnpm add jsqr.

Utilities

QR code scanner for real-time decoding

Use the QR code scanner component to decode QR codes in real time using the device camera or from uploaded images. The component uses jsQR for pure JavaScript QR decoding and supports both user and environment facing cameras.

manual startsingle scanfile upload in playground

Scan from camera

Point your camera at a QR code. The scanner decodes it in real time and displays the result below.

Result

Scan a QR code to see the result here

Or scan from an image

Try scanning this

Basic usage

Render the scanner with an onResult callback to receive decoded data. Call start() on the ref to activate the camera.

import { useRef } from "react";
import { QrScanner } from "@zentauri-ui/zentauri-components/ui/qr-scanner";
import type { QrScannerRef } from "@zentauri-ui/zentauri-components/ui/qr-scanner";

function ScannerDemo() {
  const ref = useRef<QrScannerRef>(null);
  const [result, setResult] = useState<string | null>(null);

  return (
    <div>
      <QrScanner
        ref={ref}
        onResult={(data) => setResult(data)}
        continuous={false}
      />
      <button onClick={() => ref.current?.start()}>
        Start camera
      </button>
      {result && <p>Scanned: {result}</p>}
    </div>
  );
}

Scan from an image file

Use the imperative handle to scan QR codes from uploaded images via the scanImage method.

import { useRef } from "react";
import { QrScanner } from "@zentauri-ui/zentauri-components/ui/qr-scanner";
import type { QrScannerRef } from "@zentauri-ui/zentauri-components/ui/qr-scanner";

function FileScanner() {
  const ref = useRef<QrScannerRef>(null);

  const handleFile = async (e) => {
    const file = e.target.files?.[0];
    if (!file) return;
    const data = await ref.current?.scanImage(file);
    console.log("Scanned:", data);
  };

  return (
    <>
      <QrScanner ref={ref} onResult={console.log} />
      <input type="file" accept="image/*" onChange={handleFile} />
    </>
  );
}

Qr Scanner API

Generated from the package prop types and variant definitions.

QrScanner

QrScannerProps

Variants

PropTypeDefault
appearance
defaultmuted
default

Behavior

PropTypeDefault
autoStartboolean | undefinednone
constraintsMediaTrackConstraints | undefinednone
continuousboolean | undefinednone
facingMode'user' | 'environment' | undefinednone
fallbackTextReactNodenone
loadingTextReactNodenone
noCameraTextReactNodenone
onError((error: unknown) => void) | undefinednone
onResult*(data: string) => voidnone
onStart(() => void) | undefinednone
onStop(() => void) | undefinednone
scanDelaynumber | undefinednone
Inherited HTML props
PropTypeDefault
childrenReactNodenone
classNamestring | undefinednone
idstring | undefinednone
onClickMouseEventHandler<HTMLDivElement> | undefinednone
styleCSSProperties | undefinednone
titlestring | undefinednone

QrScannerVariant

QrScannerVariantProps

Behavior

PropTypeDefault
appearance'default' | 'muted' | null | undefinednone
CSS variable overrides

QR scanner CSS variables

Override these QR scanner variables on :root, a theme selector, or a component wrapper.

6 variables

Pattern: --zui-<component>-<slot?>-<variant?>-<property>-<state?>-dark?

:root {
  --zui-qr-scanner-bg: oklch(98.4% 0.003 247.858);
  --zui-qr-scanner-status-bg: oklch(98.4% 0.003 247.858);
  --zui-qr-scanner-status-fg: oklch(55.2% 0.046 257.417);
}

/* Dark theme variables follow the same names with -dark appended. */
.dark {
  --zui-qr-scanner-bg-dark: oklch(12.9% 0.042 264.695);
  --zui-qr-scanner-status-bg-dark: oklch(12.9% 0.042 264.695);
}

What it does

The QR code scanner component accesses the device camera and continuously captures frames to decode QR codes using jsQR, a pure JavaScript QR decoder. The viewfinder overlay helps users frame the QR code correctly.

The component supports both automatic continuous scanning and single-scan mode. It also exposes a scanImage method for decoding QR codes from uploaded image files.

Composition and API

Use the required onResult callback to receive decoded data. Configure the camera with facingMode (user/environment) and additional constraints. The scanDelay prop controls the interval between frame decodes. The continuous prop determines whether scanning stops after the first result. The component exposes start, stop, and scanImage methods via ref.

Common use cases

QR code scanning is essential for mobile-friendly authentication flows, event check-ins, inventory tracking, payment verification, and any application that needs to bridge physical and digital experiences.

Accessibility

The component provides status announcements for camera state changes. The viewfinder overlay helps visually indicate the scan area. Error states provide clear feedback when camera access fails.

Next.js integration notes

Colocate examples under the App Router, keep server and client boundaries explicit, and avoid pulling interactive components into unexpected server layouts.

Set NEXT_PUBLIC_SITE_URL so canonical and Open Graph URLs resolve on deploy.

FAQ

Does the QR Scanner component work with Next.js App Router?

Yes. Import it like any other React component; the scanner uses client-side camera access and canvas processing and works seamlessly as a client boundary.

What camera permissions are needed?

The component uses navigator.mediaDevices.getUserMedia which requires camera permission. The user will be prompted to allow camera access on first use.

Can I scan from an image file?

Yes. The component exposes a scanImage method via its imperative handle that accepts a File object and returns the decoded data or null.

What happens if no camera is available?

The component gracefully falls back to a 'Camera not available' state with a customizable fallback message. It also works in environments where camera access is denied.