Yes, a component that does the job well. Code below, customise for your use case.
Note: It does not use the “cordova-plugin-qrscanner” which is broken at the time of writing.
import React, { Component } from "react";
import jsQR from "jsqr";
import $ from "jquery";
class QrCodeScanner extends Component {
constructor(props) {
super(props);
this.videoRef = React.createRef();
this.state = {
qrcode: "",
videoError: false
}
}
componentDidMount() {
this.checkCameraPermissions();
}
componentWillUnmount() {
this.stopScanner();
}
checkCameraPermissions = async () => {
var thisController = this;
cordova.plugins.permissions.requestPermission(
cordova.plugins.permissions.CAMERA,
function (status) {
if (status.hasPermission) {
console.log("Camera permission granted");
thisController.startScanner();
} else {
console.log("Camera permission denied");
thisController.state.videoError = true;
thisController.setState(thisController.state);
}
},
function () {
console.log("Error requesting camera permission");
thisController.state.videoError = true;
thisController.setState(thisController.state);
}
);
};
startScanner = () => {
navigator.mediaDevices.getUserMedia({ video: { facingMode: "environment" } })
.then((stream) => {
this.videoRef.current.srcObject = stream;
this.videoRef.current.play();
this.videoRef.current.addEventListener("loadedmetadata", this.scanFrame);
})
.catch((error) => {
console.error("Error starting scanner: ", error);
this.state.videoError = true;
});
};
stopScanner = () => {
const stream = this.videoRef.current.srcObject;
if (stream) {
const tracks = stream.getTracks();
tracks.forEach((track) => {
track.stop();
});
}
document.getElementById("qrScannerPreview").pause();
};
scanFrame = () => {
var thisController = this;
const canvas = document.createElement("canvas");
const context = canvas.getContext("2d");
const video = this.videoRef.current;
canvas.width = video.videoWidth;
canvas.height = video.videoHeight;
context.drawImage(video, 0, 0, canvas.width, canvas.height);
const imageData = context.getImageData(0, 0, canvas.width, canvas.height);
const code = jsQR(imageData.data, imageData.width, imageData.height);
if (code) {
console.log("QR code detected: ", code.data);
//Do something with the extracted code.
return;
}
}
requestAnimationFrame(this.scanFrame);
};
render() {
return (
<div style={{ height: "90vh", marginTop: "7vh" }} >
<div id="view-qrscanner" >
<div>
<div className="container-qr">
<div style={{ paddingTop: "1vh" }} >
<div align="center">
<h3 style={{ fontWeight: 'bold', fontSize: "1.28rem", color: 'white' }}>Scan your onboarding QR code</h3>
<label style={{ padding: "10vw", fontSize: "0.92rem", color: 'white' }}>Postion the QR Code a few inches away</label>
</div>
<div id="camera" className="pre_capture_frame" style={{ width: '95vw' }} />
{!this.state.videoError && <video style={{ width: "85vw", height: "85vw", margin: '4.5vw', borderRadius: '12px' }}
ref={this.videoRef} id="qrScannerPreview" />}
{this.state.videoError && <div>Could not start the video source.
Please ensure that you have granted camera permissions and that
the camera is not being used by another app.</div>}
<br />
<div id="tick" align="center" style={{ display: 'none', color: '#94bde9',
fontSize: '108px', width: '95vw' }}>
✔
</div>
</div>
</div>
</div>
</div>
</div>
);
}
}
export default QrCodeScanner;

Sreekumar (KJ) has been a hobby programmer from school days. Codemarvels is his personal blog from the year 2010, where he writes about technology, philosophy, society and a bit about physics.
He now runs a conversational AI company – DheeYantra – focusing his efforts to help businesses improve operational efficiency using digital employees powered by AI.