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.
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.
<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.
<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.
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) andAndroidManifest.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) => voidCalled with the decoded string when a QR code is recognized
onScanreq(value: string) => voidCalled with the decoded string when a QR code is recognized
onError(error: Error) => voidCalled when the camera fails or a decode error occurs
onError(error: Error) => voidCalled when the camera fails or a decode error occurs
facingMode'environment' | 'user'"'environment'"Preferred camera direction. 'environment' selects the rear camera on mobile
facingMode'environment' | 'user'"'environment'"Preferred camera direction. 'environment' selects the rear camera on mobile
formatsstring[]"['qr_code']"BarcodeDetector formats to recognize. Use ['qr_code'] for QR only, or include other 2D/1D formats supported by the browser
formatsstring[]"['qr_code']"BarcodeDetector formats to recognize. Use ['qr_code'] for QR only, or include other 2D/1D formats supported by the browser
continuousboolean"false"When true, keeps scanning after each detected code (useful for batch workflows). When false, stops after the first detection
continuousboolean"false"When true, keeps scanning after each detected code (useful for batch workflows). When false, stops after the first detection
autoStartboolean"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
autoStartboolean"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
showTorchToggleboolean"true"Show the flashlight (torch) toggle button when supported by the device
showTorchToggleboolean"true"Show the flashlight (torch) toggle button when supported by the device
showCameraSwitchboolean"true"Show the front/rear camera switch button when more than one video input is available
showCameraSwitchboolean"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
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
widthnumber | string"'100%'"Outer container width. Accepts CSS units (e.g. 320, '100%', '24rem')
widthnumber | string"'100%'"Outer container width. Accepts CSS units (e.g. 320, '100%', '24rem')
heightnumber | stringOuter container height. If omitted, the container uses aspectRatio to size itself
heightnumber | stringOuter container height. If omitted, the container uses aspectRatio to size itself
aspectRationumber"1"Aspect ratio used when height is not provided. 1 = square, 1.333 = 4:3, 1.777 = 16:9
aspectRationumber"1"Aspect ratio used when height is not provided. 1 = square, 1.333 = 4:3, 1.777 = 16:9
scanIntervalMsnumber"200"Polling interval in milliseconds. Lower values = faster detection at higher CPU/battery cost
scanIntervalMsnumber"200"Polling interval in milliseconds. Lower values = faster detection at higher CPU/battery cost
pauseAfterScanboolean"true"When continuous=false, stop the camera and return to idle after the first successful scan
pauseAfterScanboolean"true"When continuous=false, stop the camera and return to idle after the first successful scan
unsupportedMessageReactNodeCustom message shown when the browser does not support BarcodeDetector
unsupportedMessageReactNodeCustom message shown when the browser does not support BarcodeDetector
deniedMessageReactNodeCustom message shown when the user denies camera permission
deniedMessageReactNodeCustom message shown when the user denies camera permission
startLabelReactNode"'Start scanning'"Label for the start button
startLabelReactNode"'Start scanning'"Label for the start button
idstringHTML id attribute
idstringHTML id attribute
classNamestringAdditional CSS classes
classNamestringAdditional CSS classes
ariaLabelstring"'QR code scanner'"Accessible label for the scanner region
ariaLabelstring"'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.