Fluxo UIFluxo UIv0.4.1

QR Scanner

Turn the device camera on, decode QR codes in real time, and return the value — entirely with native browser APIs and no third-party dependency.

Basic Usage

Default Scanner

Click Start scanning, point your camera at any QR code, and the decoded value appears below. Uses the native BarcodeDetector API — no third-party dependency.

Last decoded value— nothing scanned yet —
import { QrScanner } from 'fluxo-ui';

<QrScanner
  onScan={(value) => console.log('Decoded:', value)}
  onError={(err) => console.error(err)}
/>

Continuous Mode

Continuous Mode

With continuous=true, the scanner keeps reading after each successful detection — useful for batch entry. Duplicate codes are debounced for ~1.5 seconds.

Scan history (0)No scans yet — start the scanner and aim at QR codes.
<QrScanner
  continuous
  onScan={(value) => setHistory((h) => [value, ...h])}
/>

Customization

Overlay & Aspect Ratio

Switch between the dimmed mask and bracket-only frame overlays, change aspect ratio, and pick the front or rear camera.

Last decoded value
<QrScanner
  overlay="frame"
  aspectRatio={1.333}
  facingMode="environment"
  showTorchToggle
  showCameraSwitch
  onScan={handleScan}
/>

Imperative API

Imperative Control

Use the ref handle to start/stop the scanner from outside the component, or call the lower-level scanQrCodeOnce utility directly with your own video element.

Last: —
import { scanQrCodeOnce } from 'fluxo-ui/utils';

// Lower-level utility — bring your own video element
const value = await scanQrCodeOnce({ video: videoRef.current });

// Or drive the component imperatively
const ref = useRef<QrScannerHandle>(null);
ref.current?.start();
ref.current?.stop();

Browser Support

QrScanner is built on the native BarcodeDetector and getUserMedia APIs. No JavaScript decoder is bundled.

  • Chrome & Edge (desktop and Android) — supported.
  • Safari 17+ on iOS and macOS — supported.
  • Firefox — not yet supported. The component renders an "unsupported" message.
  • Requires a secure (HTTPS or localhost) context.
  • Inside Capacitor / Ionic apps it works on the same matrix — declare camera permission in Info.plist (iOS) and AndroidManifest.xml.
// Feature detection
import { isQrScanSupported, isCameraAvailable } from 'fluxo-ui/utils';

if (isQrScanSupported() && isCameraAvailable()) {
  // safe to render <QrScanner />
}

Import

import { QrScanner } from 'fluxo-ui';
import type { QrScannerProps, QrScannerHandle, QrScannerStatus } from 'fluxo-ui';
import { startQrScan, scanQrCodeOnce, isQrScanSupported, isCameraAvailable, listVideoInputDevices } from 'fluxo-ui/utils';

Props

onScanreq
(value: string) => void

Called with the decoded string when a QR code is recognized

onError
(error: Error) => void

Called when the camera fails or a decode error occurs

facingMode
'environment' | 'user'"'environment'"

Preferred camera direction. 'environment' selects the rear camera on mobile

formats
string[]"['qr_code']"

BarcodeDetector formats to recognize. Use ['qr_code'] for QR only, or include other 2D/1D formats supported by the browser

continuous
boolean"false"

When true, keeps scanning after each detected code (useful for batch workflows). When false, stops after the first detection

autoStart
boolean"false"

When true, requests the camera and starts scanning on mount. Note: iOS Safari may require a user gesture, so a Start button is the safer default

showTorchToggle
boolean"true"

Show the flashlight (torch) toggle button when supported by the device

showCameraSwitch
boolean"true"

Show the front/rear camera switch button when more than one video input is available

overlay
'frame' | 'mask' | 'none'"'mask'"

Visual overlay style. 'mask' dims the area around the framing reticle, 'frame' shows just the corner brackets, 'none' hides the overlay

width
number | string"'100%'"

Outer container width. Accepts CSS units (e.g. 320, '100%', '24rem')

height
number | string

Outer container height. If omitted, the container uses aspectRatio to size itself

aspectRatio
number"1"

Aspect ratio used when height is not provided. 1 = square, 1.333 = 4:3, 1.777 = 16:9

scanIntervalMs
number"200"

Polling interval in milliseconds. Lower values = faster detection at higher CPU/battery cost

pauseAfterScan
boolean"true"

When continuous=false, stop the camera and return to idle after the first successful scan

unsupportedMessage
ReactNode

Custom message shown when the browser does not support BarcodeDetector

deniedMessage
ReactNode

Custom message shown when the user denies camera permission

startLabel
ReactNode"'Start scanning'"

Label for the start button

id
string

HTML id attribute

className
string

Additional CSS classes

ariaLabel
string"'QR code scanner'"

Accessible label for the scanner region

Features

No Dependencies

Uses the native BarcodeDetector + getUserMedia APIs — zero third-party code.

Mobile First

Rear camera by default, torch toggle, camera switch, 44×44 touch targets, and a focus reticle that adapts to portrait or landscape.

Single or Continuous

Scan once and stop, or keep reading codes back-to-back with built-in duplicate debouncing.

Themeable

Reticle and controls inherit your active brand theme via --eui-* CSS variables — works in light and dark mode.