Skip to content

Instantly share code, notes, and snippets.

@yehonatan56
Created May 25, 2025 17:52
Show Gist options
  • Select an option

  • Save yehonatan56/3e780a45179fce288cd89786babbf7b2 to your computer and use it in GitHub Desktop.

Select an option

Save yehonatan56/3e780a45179fce288cd89786babbf7b2 to your computer and use it in GitHub Desktop.
import { useEffect, useRef, useState } from 'react';
import { BrowserMultiFormatReader, IScannerControls } from '@zxing/browser';
const BarcodeScanner = ({ onResult }: { onResult: (text: string) => void }) => {
const videoRef = useRef<HTMLVideoElement | null>(null);
const codeReader = useRef(new BrowserMultiFormatReader());
const [controls, setControls] = useState<IScannerControls | null>(null);
const [devices, setDevices] = useState<MediaDeviceInfo[]>([]);
const [selectedDeviceId, setSelectedDeviceId] = useState<string | null>(null);
const [scanning, setScanning] = useState(false);
const [error, setError] = useState('');
useEffect(() => {
setScanning(true);
navigator.mediaDevices
.getUserMedia({ video: true })
.then(() =>
BrowserMultiFormatReader.listVideoInputDevices().then((videoDevices) => {
setDevices(videoDevices);
if (videoDevices.length > 0) {
setSelectedDeviceId(videoDevices[0].deviceId);
}
})
)
.catch((err) => {
console.error('Access denied:', err);
setError('Camera access must be allowed in the browser.');
});
}, []);
useEffect(() => {
if (!selectedDeviceId || !videoRef.current) return;
// Stop previous scanning session
if (controls) {
controls.stop();
}
if (scanning) {
codeReader.current
.decodeFromVideoDevice(selectedDeviceId, videoRef.current, (result, _error, ctrl) => {
if (result) {
onScan(result.getText());
}
if (ctrl && ctrl !== controls) {
setControls(ctrl); // Save controls for future stop
}
})
.catch((err) => {
console.error('Scanning error:', err);
});
}
return () => {
if (controls) {
controls.stop();
}
};
}, [selectedDeviceId]);
const onScan = (text: string) => {
console.log('Scan result:', text);
onResult(text);
setScanning(false);
// Stop scanning after a successful scan
if (controls) {
controls.stop();
}
};
return (
<div>
{error && <p style={{ color: 'red' }}>{error}</p>}
{scanning ? (
<>
{devices.length > 1 && (
<select onChange={(e) => setSelectedDeviceId(e.target.value)} value={selectedDeviceId || ''}>
{devices.map((device, idx) => (
<option key={idx} value={device.deviceId}>
{device.label || `Camera ${idx + 1}`}
</option>
))}
</select>
)}
<video ref={videoRef} style={{ width: '100%', maxWidth: 400, transform: 'scaleX(-1)' }} />
</>
) : (
<p style={{ color: 'green' }}>Scan completed successfully!</p>
)}
</div>
);
};
export default BarcodeScanner;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment