How to Build a QR Code Scanner App With React Native
The simplest way to build a QR code scanning app in React Native today is to use Expo and the expo-camera library, whose CameraView component scans QR and other barcodes natively. You install the package, request camera permission with the useCameraPermissions hook, set barcodeScannerSettings to QR, and handle each result in onBarcodeScanned. The rest is validation and user experience.
Key takeaways
- expo-camera handles camera preview and QR detection; the older expo-barcode-scanner approach has been superseded.
- Always check permission state before rendering the camera and give users a clear way to grant it.
- Throttle or lock scanning after a hit, or the callback fires many times for one code.
- Treat scanned data as untrusted input: validate format before acting on it.
- Plan for offline use and low light if the app will run in warehouses, clinics or field sites.
What you need before you start
- Node.js LTS and a code editor.
- A new Expo project created with the official create-expo-app command.
- A physical phone with the Expo Go app or a development build, because simulators have limited camera support.
The React Native documentation recommends using a framework such as Expo for new apps, which saves you from configuring native camera modules by hand. If you need deeper native control later, bare React Native projects can use community libraries such as VisionCamera, but most business scanners do not need that.
Create the project and add the camera package:
npx create-expo-app qr-scanner
cd qr-scanner
npx expo install expo-camera
How do you build the scanner screen?
The component below checks permission, shows the camera, scans only QR codes and locks after the first successful scan so the user can confirm the result.
import { useState } from 'react';
import { Button, Text, View, StyleSheet } from 'react-native';
import { CameraView, useCameraPermissions } from 'expo-camera';
export default function Scanner() {
const [permission, requestPermission] = useCameraPermissions();
const [result, setResult] = useState(null);
if (!permission) return <View />;
if (!permission.granted) {
return (
<View style={styles.center}>
<Text>Camera access is needed to scan QR codes.</Text>
<Button title="Allow camera" onPress={requestPermission} />
</View>
);
}
return (
<View style={{ flex: 1 }}>
<CameraView
style={{ flex: 1 }}
barcodeScannerSettings={{ barcodeTypes: ['qr'] }}
onBarcodeScanned={result ? undefined : (scan) => setResult(scan.data)}
/>
{result && (
<View style={styles.panel}>
<Text>{result}</Text>
<Button title="Scan again" onPress={() => setResult(null)} />
</View>
)}
</View>
);
}
const styles = StyleSheet.create({
center: { flex: 1, alignItems: 'center', justifyContent: 'center', padding: 16 },
panel: { padding: 16, backgroundColor: 'white' },
});
Passing undefined to onBarcodeScanned once a result exists stops repeated callbacks while the code is still in view. Each scan result includes the barcode type and its data, plus bounds you can use to draw a highlight box.
Making the scanner production-ready
Validate what you scan
A QR code can contain any text, including malicious links. Decide what format your app expects, such as an asset ID pattern, a signed token or a URL on your own domain, and reject everything else with a clear message. Never open arbitrary URLs automatically.
Handle permissions and platform settings
Add a camera usage description in your app configuration so iOS shows a meaningful prompt. If a user denies permission permanently, show instructions for enabling it in device settings instead of a dead screen.
Plan for poor connectivity
Scanning works offline, but whatever you do with the result may not. Queue scans locally with a timestamp and sync when the network returns. This is the same design problem time clock apps face, which we discuss in offline time clock punching.
Design for real environments
Test in low light, with damaged or glossy labels and at different distances. Add a torch toggle (CameraView supports an enableTorch prop) and audible or haptic feedback on success.
Build checklist
| Item | Why it matters |
|---|---|
| Permission states handled (undetermined, granted, denied) | Avoids blank screens and app store rejections |
| Scan lock or debounce | Prevents duplicate records from one code |
| Input validation | Blocks malformed or malicious payloads |
| Offline queue | Keeps scans from being lost on weak networks |
| Torch and feedback | Faster, more reliable scanning on the floor |
| Unmount camera when screen loses focus | Only one camera preview can be active at a time |
| Device testing on iOS and Android | Camera behavior differs by platform |
If your team needs a scanner inside a larger workflow but lacks mobile developers, compare building from scratch with platform options; our overview of how low-code helps teams build apps quickly covers the trade-offs, and our piece on why mobile app development matters for business covers the business case.
Frequently Asked Questions
Do I need Expo to scan QR codes in React Native?
No, but it is the fastest route. Expo's camera package bundles native scanning and works with Expo Go for testing. Bare React Native apps can use community camera libraries that offer code scanning, which give more control over frame processing but require native setup on iOS and Android.
Why does onBarcodeScanned fire multiple times?
The camera analyzes frames continuously, so a code that stays in view is detected repeatedly. Store the first result in state and set the callback to undefined until the user chooses to scan again, or ignore repeat values within a short time window. This prevents duplicate records and repeated network calls.
Can the app scan barcodes other than QR codes?
Yes. The barcodeTypes array accepts other formats such as EAN-13, UPC-A and Code 128. Limit the list to the formats you actually use, because scanning fewer types reduces false reads and makes your validation logic simpler and easier to test.
Next steps
Build the screen above, test it on two physical devices, then add validation and an offline queue before connecting it to your backend. Write down the exact payload format your QR codes will use so labels and app stay in sync.
Last reviewed: October 2026.
Sources
- Camera (expo-camera) reference, Expo Documentation
- Create a project, Expo Documentation
- Get Started with React Native, React Native
Comments
Post a Comment