Expo Camera provides comprehensive barcode and QR code scanning capabilities supporting multiple barcode formats with real-time detection during camera preview and static image scanning from URLs.
Configure the camera component to detect barcodes in real-time during camera preview.
/**
* Configure barcode scanning settings for the camera
*/
interface BarcodeSettings {
/**
* Array of barcode types to detect
*/
barcodeTypes: BarcodeType[];
}
/**
* Callback for barcode detection events
* @param result The detected barcode information
*/
type BarcodeScanningCallback = (result: BarcodeScanningResult) => void;Scan barcodes from static images via URL.
/**
* Scan barcodes from an image at the given URL
* @param url URL of the image to scan
* @param barcodeTypes Array of barcode types to detect (defaults to ['qr'])
* @returns Promise resolving to array of detected barcodes
*/
function scanFromURLAsync(
url: string,
barcodeTypes?: BarcodeType[]
): Promise<BarcodeScanningResult[]>;Native iOS barcode scanner with advanced features.
/**
* Launch the modern iOS barcode scanner
* @returns Promise resolving when scanner is launched
*/
launchModernScanner(): Promise<void>;
/**
* Launch the barcode scanner with configuration options (iOS only)
* @param options Scanning configuration options
* @returns Promise resolving when scanner launches
*/
launchScanner(options?: ScanningOptions): Promise<void>;
/**
* Dismiss the active barcode scanner (iOS only)
* @returns Promise resolving when scanner is dismissed
*/
dismissScanner(): Promise<void>;
/**
* Configuration options for iOS barcode scanner
*/
interface ScanningOptions {
/**
* Types of barcodes to scan for
*/
barcodeTypes: BarcodeType[];
/**
* Enable pinch-to-zoom gesture
* @default true
*/
isPinchToZoomEnabled?: boolean;
/**
* Show guidance text over video
* @default true
*/
isGuidanceEnabled?: boolean;
/**
* Highlight recognized items
* @default false
*/
isHighlightingEnabled?: boolean;
}Complete structure of barcode scanning results.
interface BarcodeScanningResult {
/**
* Detected barcode type
*/
type: string;
/**
* Parsed barcode data
*/
data: string;
/**
* Raw barcode data (Android only)
*/
raw?: string;
/**
* Corner points of the barcode bounding box
* Order varies by platform:
* - Android: topLeft, topRight, bottomRight, bottomLeft
* - iOS: bottomLeft, bottomRight, topLeft, topRight
* - Web: topLeft, bottomLeft, topRight, bottomRight
*/
cornerPoints: BarcodePoint[];
/**
* Bounding box of the detected barcode
*/
bounds: BarcodeBounds;
/**
* Platform-specific extra information (Android only)
*/
extra?: AndroidBarcode;
}
interface BarcodePoint {
x: number;
y: number;
}
interface BarcodeBounds {
/**
* Origin point of bounding box
*/
origin: BarcodePoint;
/**
* Size of bounding box
*/
size: BarcodeSize;
}
interface BarcodeSize {
width: number;
height: number;
}All supported barcode formats across platforms.
type BarcodeType =
| 'aztec'
| 'ean13'
| 'ean8'
| 'qr'
| 'pdf417'
| 'upc_e'
| 'datamatrix'
| 'code39'
| 'code93'
| 'itf14'
| 'codabar'
| 'code128'
| 'upc_a';Extended barcode information available on Android platform.
/**
* Enhanced barcode information for Android platform
*/
type AndroidBarcode =
| ContactInfoBarcode
| GeoPointBarcode
| SmsBarcode
| UrlBarcode
| CalendarEventBarcode
| DriverLicenseBarcode
| EmailBarcode
| PhoneBarcode
| WifiBarcode;
interface ContactInfoBarcode {
type: 'contactInfo';
firstName?: string;
middleName?: string;
lastName?: string;
title?: string;
organization?: string;
email?: string;
phone?: string;
url?: string;
address?: string;
}
interface GeoPointBarcode {
type: 'geoPoint';
lat: string;
lng: string;
}
interface SmsBarcode {
type: 'sms';
phoneNumber?: string;
message?: string;
}
interface UrlBarcode {
type: 'url';
url?: string;
}
interface CalendarEventBarcode {
type: 'calendarEvent';
summary?: string;
description?: string;
location?: string;
start?: string;
end?: string;
}
interface DriverLicenseBarcode {
type: 'driverLicense';
firstName?: string;
middleName?: string;
lastName?: string;
licenseNumber?: string;
expiryDate?: string;
issueDate?: string;
addressStreet?: string;
addressCity?: string;
addressState?: string;
}
interface EmailBarcode {
type: 'email';
address?: string;
subject?: string;
body?: string;
}
interface PhoneBarcode {
type: 'phone';
number?: string;
phoneNumberType?: string;
}
interface WifiBarcode {
type: 'wifi';
ssid?: string;
password?: string;
encryptionType?: string;
}import React, { useState } from 'react';
import { View, Text, StyleSheet, Alert } from 'react-native';
import { CameraView, BarcodeScanningResult } from 'expo-camera';
export function QRCodeScanner() {
const [scannedData, setScannedData] = useState<string | null>(null);
const handleBarcodeScanned = (result: BarcodeScanningResult) => {
console.log('Barcode scanned:', result);
setScannedData(result.data);
Alert.alert(
'QR Code Detected',
result.data,
[
{ text: 'OK', onPress: () => setScannedData(null) }
]
);
};
return (
<View style={styles.container}>
<CameraView
style={styles.camera}
facing="back"
onBarcodeScanned={handleBarcodeScanned}
barcodeScannerSettings={{
barcodeTypes: ['qr']
}}
/>
{scannedData && (
<View style={styles.resultContainer}>
<Text style={styles.resultText}>
Last scanned: {scannedData}
</Text>
</View>
)}
</View>
);
}
const styles = StyleSheet.create({
container: { flex: 1 },
camera: { flex: 1 },
resultContainer: {
position: 'absolute',
bottom: 100,
left: 20,
right: 20,
backgroundColor: 'rgba(0,0,0,0.8)',
padding: 15,
borderRadius: 10,
},
resultText: {
color: 'white',
textAlign: 'center',
},
});import React, { useState } from 'react';
import { View, Text, StyleSheet, FlatList } from 'react-native';
import { CameraView, BarcodeScanningResult, BarcodeType } from 'expo-camera';
interface ScannedBarcode {
id: string;
type: string;
data: string;
timestamp: Date;
}
export function MultiFormatBarcodeScanner() {
const [scannedBarcodes, setScannedBarcodes] = useState<ScannedBarcode[]>([]);
const supportedTypes: BarcodeType[] = [
'qr',
'ean13',
'ean8',
'code128',
'code39',
'upc_a',
'upc_e',
'pdf417',
'aztec',
'datamatrix'
];
const handleBarcodeScanned = (result: BarcodeScanningResult) => {
// Avoid duplicate scans of the same barcode
const isDuplicate = scannedBarcodes.some(
barcode => barcode.data === result.data && barcode.type === result.type
);
if (!isDuplicate) {
const newBarcode: ScannedBarcode = {
id: `${Date.now()}-${result.type}`,
type: result.type,
data: result.data,
timestamp: new Date(),
};
setScannedBarcodes(prev => [newBarcode, ...prev].slice(0, 10)); // Keep last 10
console.log('New barcode detected:', {
type: result.type,
data: result.data,
bounds: result.bounds,
cornerPoints: result.cornerPoints,
});
}
};
const renderBarcodeItem = ({ item }: { item: ScannedBarcode }) => (
<View style={styles.barcodeItem}>
<Text style={styles.barcodeType}>{item.type.toUpperCase()}</Text>
<Text style={styles.barcodeData}>{item.data}</Text>
<Text style={styles.barcodeTime}>
{item.timestamp.toLocaleTimeString()}
</Text>
</View>
);
return (
<View style={styles.container}>
<CameraView
style={styles.camera}
facing="back"
onBarcodeScanned={handleBarcodeScanned}
barcodeScannerSettings={{
barcodeTypes: supportedTypes
}}
/>
<View style={styles.overlay}>
<Text style={styles.instructions}>
Point camera at barcode or QR code
</Text>
<Text style={styles.supportedFormats}>
Supports: QR, EAN, UPC, Code128, Code39, PDF417, Aztec, DataMatrix
</Text>
</View>
{scannedBarcodes.length > 0 && (
<View style={styles.resultsContainer}>
<Text style={styles.resultsTitle}>Recent Scans:</Text>
<FlatList
data={scannedBarcodes}
renderItem={renderBarcodeItem}
keyExtractor={item => item.id}
style={styles.resultsList}
/>
</View>
)}
</View>
);
}
const styles = StyleSheet.create({
container: { flex: 1 },
camera: { flex: 1 },
overlay: {
position: 'absolute',
top: 100,
left: 20,
right: 20,
alignItems: 'center',
},
instructions: {
color: 'white',
fontSize: 18,
fontWeight: 'bold',
textAlign: 'center',
backgroundColor: 'rgba(0,0,0,0.7)',
padding: 10,
borderRadius: 10,
},
supportedFormats: {
color: 'white',
fontSize: 12,
textAlign: 'center',
backgroundColor: 'rgba(0,0,0,0.5)',
padding: 5,
borderRadius: 5,
marginTop: 10,
},
resultsContainer: {
position: 'absolute',
bottom: 0,
left: 0,
right: 0,
backgroundColor: 'rgba(255,255,255,0.95)',
maxHeight: 200,
},
resultsTitle: {
fontSize: 16,
fontWeight: 'bold',
padding: 10,
textAlign: 'center',
},
resultsList: {
maxHeight: 150,
},
barcodeItem: {
padding: 10,
borderBottomWidth: 1,
borderBottomColor: '#eee',
},
barcodeType: {
fontSize: 12,
fontWeight: 'bold',
color: '#007AFF',
},
barcodeData: {
fontSize: 14,
marginVertical: 2,
},
barcodeTime: {
fontSize: 10,
color: '#666',
},
});import React, { useState } from 'react';
import { View, TextInput, TouchableOpacity, Text, StyleSheet, Alert } from 'react-native';
import { scanFromURLAsync, BarcodeScanningResult, BarcodeType } from 'expo-camera';
export function StaticImageScanner() {
const [imageUrl, setImageUrl] = useState('');
const [isScanning, setIsScanning] = useState(false);
const [results, setResults] = useState<BarcodeScanningResult[]>([]);
const scanImageURL = async () => {
if (!imageUrl.trim()) {
Alert.alert('Error', 'Please enter an image URL');
return;
}
setIsScanning(true);
try {
const barcodeTypes: BarcodeType[] = [
'qr', 'ean13', 'ean8', 'code128', 'code39', 'upc_a', 'upc_e'
];
const detectedBarcodes = await scanFromURLAsync(imageUrl, barcodeTypes);
setResults(detectedBarcodes);
if (detectedBarcodes.length === 0) {
Alert.alert('No Barcodes Found', 'No barcodes were detected in the image');
} else {
Alert.alert(
'Barcodes Found',
`Found ${detectedBarcodes.length} barcode(s)`
);
}
} catch (error) {
Alert.alert('Scan Failed', `Error scanning image: ${error.message}`);
} finally {
setIsScanning(false);
}
};
const scanQROnly = async () => {
if (!imageUrl.trim()) return;
setIsScanning(true);
try {
// Scan for QR codes only (iOS limitation note)
const qrCodes = await scanFromURLAsync(imageUrl, ['qr']);
setResults(qrCodes);
if (qrCodes.length > 0) {
Alert.alert('QR Code Found', qrCodes[0].data);
} else {
Alert.alert('No QR Code Found', 'No QR codes detected in the image');
}
} catch (error) {
Alert.alert('Scan Failed', error.message);
} finally {
setIsScanning(false);
}
};
return (
<View style={styles.container}>
<Text style={styles.title}>Static Image Barcode Scanner</Text>
<TextInput
style={styles.input}
placeholder="Enter image URL"
value={imageUrl}
onChangeText={setImageUrl}
multiline
/>
<View style={styles.buttonContainer}>
<TouchableOpacity
style={[styles.button, isScanning && styles.disabledButton]}
onPress={scanImageURL}
disabled={isScanning}
>
<Text style={styles.buttonText}>
{isScanning ? 'Scanning...' : 'Scan All Types'}
</Text>
</TouchableOpacity>
<TouchableOpacity
style={[styles.button, isScanning && styles.disabledButton]}
onPress={scanQROnly}
disabled={isScanning}
>
<Text style={styles.buttonText}>
{isScanning ? 'Scanning...' : 'Scan QR Only'}
</Text>
</TouchableOpacity>
</View>
{results.length > 0 && (
<View style={styles.resultsContainer}>
<Text style={styles.resultsTitle}>Scan Results:</Text>
{results.map((result, index) => (
<View key={index} style={styles.resultItem}>
<Text style={styles.resultType}>Type: {result.type}</Text>
<Text style={styles.resultData}>Data: {result.data}</Text>
{result.bounds && (
<Text style={styles.resultBounds}>
Bounds: {result.bounds.size.width} x {result.bounds.size.height}
</Text>
)}
</View>
))}
</View>
)}
<Text style={styles.note}>
Note: On iOS, only QR codes are supported for URL scanning
</Text>
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
padding: 20,
backgroundColor: '#f5f5f5',
},
title: {
fontSize: 20,
fontWeight: 'bold',
textAlign: 'center',
marginBottom: 20,
},
input: {
borderWidth: 1,
borderColor: '#ddd',
borderRadius: 8,
padding: 15,
backgroundColor: 'white',
marginBottom: 20,
minHeight: 100,
textAlignVertical: 'top',
},
buttonContainer: {
flexDirection: 'row',
justifyContent: 'space-between',
marginBottom: 20,
},
button: {
backgroundColor: '#007AFF',
padding: 15,
borderRadius: 8,
flex: 0.48,
},
disabledButton: {
backgroundColor: '#ccc',
},
buttonText: {
color: 'white',
textAlign: 'center',
fontWeight: 'bold',
},
resultsContainer: {
backgroundColor: 'white',
borderRadius: 8,
padding: 15,
marginBottom: 20,
},
resultsTitle: {
fontSize: 16,
fontWeight: 'bold',
marginBottom: 10,
},
resultItem: {
marginBottom: 15,
paddingBottom: 15,
borderBottomWidth: 1,
borderBottomColor: '#eee',
},
resultType: {
fontWeight: 'bold',
color: '#007AFF',
},
resultData: {
marginTop: 5,
},
resultBounds: {
marginTop: 5,
fontSize: 12,
color: '#666',
},
note: {
fontSize: 12,
color: '#666',
textAlign: 'center',
fontStyle: 'italic',
},
});import React, { useEffect } from 'react';
import { View, TouchableOpacity, Text, StyleSheet, Platform } from 'react-native';
import { CameraView, ScanningOptions, ScanningResult } from 'expo-camera';
export function ModernBarcodeScanner() {
const cameraRef = React.useRef<CameraView>(null);
// Listen for modern scanner results
useEffect(() => {
if (Platform.OS !== 'ios') return;
// Set up event listener for modern barcode scanning
const subscription = CameraManager.addListener(
'onModernBarcodeScanned',
(event: ScanningResult) => {
console.log('Modern scanner result:', event);
// Handle the scanned result
}
);
return () => subscription?.remove();
}, []);
const launchModernScanner = async () => {
if (Platform.OS !== 'ios') {
console.warn('Modern scanner is only available on iOS');
return;
}
if (!cameraRef.current) return;
try {
await cameraRef.current.launchModernScanner();
} catch (error) {
console.error('Failed to launch modern scanner:', error);
}
};
const launchScannerWithOptions = async () => {
if (Platform.OS !== 'ios') return;
try {
const options: ScanningOptions = {
barcodeTypes: ['qr', 'ean13', 'code128'],
isPinchToZoomEnabled: true,
isGuidanceEnabled: true,
isHighlightingEnabled: true,
};
// Note: This would typically be called on a CameraManager instance
await CameraManager.launchScanner(options);
} catch (error) {
console.error('Failed to launch scanner with options:', error);
}
};
const dismissScanner = async () => {
if (Platform.OS !== 'ios') return;
try {
await CameraManager.dismissScanner();
} catch (error) {
console.error('Failed to dismiss scanner:', error);
}
};
if (Platform.OS !== 'ios') {
return (
<View style={styles.container}>
<Text style={styles.unavailable}>
Modern scanner is only available on iOS
</Text>
</View>
);
}
return (
<View style={styles.container}>
<CameraView
ref={cameraRef}
style={styles.camera}
facing="back"
/>
<View style={styles.controls}>
<TouchableOpacity style={styles.button} onPress={launchModernScanner}>
<Text style={styles.buttonText}>Launch Modern Scanner</Text>
</TouchableOpacity>
<TouchableOpacity style={styles.button} onPress={launchScannerWithOptions}>
<Text style={styles.buttonText}>Launch with Options</Text>
</TouchableOpacity>
<TouchableOpacity style={styles.button} onPress={dismissScanner}>
<Text style={styles.buttonText}>Dismiss Scanner</Text>
</TouchableOpacity>
</View>
</View>
);
}
const styles = StyleSheet.create({
container: { flex: 1 },
camera: { flex: 1 },
unavailable: {
textAlign: 'center',
fontSize: 16,
color: '#666',
marginTop: 100,
},
controls: {
position: 'absolute',
bottom: 50,
left: 20,
right: 20,
},
button: {
backgroundColor: '#007AFF',
padding: 15,
borderRadius: 8,
marginBottom: 10,
},
buttonText: {
color: 'white',
textAlign: 'center',
fontWeight: 'bold',
},
});