
React Native Vision Camera has revolutionized mobile camera functionality in React Native applications. This powerful library offers advanced features that go far beyond basic photo capture, making it the go-to choice for developers who need professional-grade camera capabilities.
What is React Native Vision Camera?
React Native Vision Camera is a high-performance camera library built specifically for React Native applications. Unlike the deprecated react-native-camera, Vision Camera is designed from the ground up to leverage modern smartphone camera capabilities while providing excellent performance and developer experience.
Key Features Overview
šļø QR/Barcode Scanner - Built-in code scanning capabilities
šļø Multiple Resolutions - Support for 4K/8K images and various aspect ratios
ā±ļø Variable FPS - Customizable frame rates from 30 to 240 FPS
š§© Frame Processors - JavaScript worklets for real-time image processing
šØ GPU Acceleration - Custom C++/GPU accelerated video pipeline
š Smooth Zooming - Integrated with React Native Reanimated
š Advanced Modes - HDR, Night mode, and custom camera configurations
ā” Performance Optimized - Battery-efficient background camera mode
Installation & Setup
Step 1: Install the Package
For React Native CLI projects:
npm install react-native-vision-camera
cd ios && pod install
For Expo projects:
npx expo install react-native-vision-camera
Step 2: iOS Configuration
Add camera permissions to your ios/YourApp/Info.plist:
<key>NSCameraUsageDescription</key>
<string>$(PRODUCT_NAME) needs access to your Camera.</string>
<key>NSMicrophoneUsageDescription</key>
<string>$(PRODUCT_NAME) needs access to your Microphone.</string>
Step 3: Android Configuration
Update android/app/src/main/AndroidManifest.xml:
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.RECORD_AUDIO" />
Step 4: Expo Configuration
For Expo managed workflow, add to your app.json:
{
"expo": {
"plugins": [
[
"react-native-vision-camera",
{
"cameraPermissionText": "$(PRODUCT_NAME) needs access to your Camera.",
"enableMicrophonePermission": true,
"microphonePermissionText": "$(PRODUCT_NAME) needs access to your Microphone."
}
]
]
}
}
npx expo prebuild
Basic Camera Implementation
Here\'s a complete basic camera setup with permission handling:
import React, { useEffect } from "react";
import {
View,
Text,
StyleSheet,
TouchableOpacity,
Alert,
Linking
} from "react-native";
import {
Camera,
useCameraDevice,
useCameraPermission
} from "react-native-vision-camera";
const CameraApp = () => {
const device = useCameraDevice("back");
const { hasPermission, requestPermission } = useCameraPermission();
useEffect(() => {
checkPermissions();
}, []);
const checkPermissions = async () => {
if (!hasPermission) {
const granted = await requestPermission();
if (!granted) {
Alert.alert(
"Camera Permission Required",
"Please enable camera permission in settings to use this feature.",
[
{ text: "Cancel", style: "cancel" },
{ text: "Open Settings", onPress: () => Linking.openSettings() }
]
);
}
}
};
if (!hasPermission) {
return (
<View style={styles.permissionContainer}>
<Text style={styles.permissionText}>
Camera permission is required
</Text>
<TouchableOpacity
onPress={requestPermission}
style={styles.permissionButton}>
<Text style={styles.buttonText}>Grant Permission</Text>
</TouchableOpacity>
</View>
);
}
if (!device) {
return (
<View style={styles.permissionContainer}>
<Text style={styles.permissionText}>Camera device not found</Text>
</View>
);
}
return (
<View style={styles.container}>
<Camera
style={StyleSheet.absoluteFill}
device={device}
isActive={true}
photo={true}
video={true}
/>
</View>
);
};
const styles = StyleSheet.create({
container: {
flex: 1,
},
permissionContainer: {
flex: 1,
justifyContent: "center",
alignItems: "center",
backgroundColor: "#000",
},
permissionText: {
color: "white",
fontSize: 18,
textAlign: "center",
marginBottom: 20,
},
permissionButton: {
backgroundColor: "#007AFF",
paddingHorizontal: 20,
paddingVertical: 12,
borderRadius: 8,
},
buttonText: {
color: "white",
fontSize: 16,
fontWeight: "bold",
},
});
export default CameraApp;
Video Recording Implementation
import React, { useRef, useState, useCallback } from "react";
import { View, TouchableOpacity, Text, StyleSheet } from "react-native";
import { Camera, useCameraDevice, useCameraPermission } from "react-native-vision-camera";
const VideoRecordingScreen = () => {
const camera = useRef(null);
const device = useCameraDevice("back");
const { hasPermission } = useCameraPermission();
const [isRecording, setIsRecording] = useState(false);
const startRecording = useCallback(async () => {
try {
setIsRecording(true);
camera.current?.startRecording({
flash: "off",
onRecordingFinished: (video) => {
console.log("Video recorded:", video.path);
setIsRecording(false);
},
onRecordingError: (error) => {
console.error("Recording error:", error);
setIsRecording(false);
},
});
} catch (error) {
console.error("Start recording error:", error);
setIsRecording(false);
}
}, []);
const stopRecording = useCallback(async () => {
try {
await camera.current?.stopRecording();
} catch (error) {
console.error("Stop recording error:", error);
}
}, []);
const toggleRecording = useCallback(() => {
if (isRecording) {
stopRecording();
} else {
startRecording();
}
}, [isRecording, startRecording, stopRecording]);
if (!hasPermission || !device) {
return <View style={styles.container} />;
}
return (
<View style={styles.container}>
<Camera
ref={camera}
style={StyleSheet.absoluteFill}
device={device}
isActive={true}
video={true}
audio={true}
/>
<View style={styles.controlsContainer}>
<TouchableOpacity
style={[
styles.recordButton,
isRecording && styles.recordButtonActive
]}
onPress={toggleRecording}
>
<Text style={styles.recordButtonText}>
{isRecording ? "Stop" : "Record"}
</Text>
</TouchableOpacity>
</View>
</View>
);
};
const styles = StyleSheet.create({
container: {
flex: 1,
},
controlsContainer: {
position: "absolute",
bottom: 50,
left: 0,
right: 0,
alignItems: "center",
},
recordButton: {
backgroundColor: "red",
paddingHorizontal: 30,
paddingVertical: 15,
borderRadius: 25,
},
recordButtonActive: {
backgroundColor: "darkred",
},
recordButtonText: {
color: "white",
fontSize: 16,
fontWeight: "bold",
},
});
export default VideoRecordingScreen;
QR/Barcode Scanning Implementation
import React, { useState } from "react";
import {
View,
Text,
StyleSheet,
TouchableOpacity,
Alert
} from "react-native";
import {
Camera,
useCameraDevice,
useCameraPermission,
useCodeScanner
} from "react-native-vision-camera";
const QRScannerScreen = () => {
const device = useCameraDevice("back");
const { hasPermission } = useCameraPermission();
const [scannedData, setScannedData] = useState(null);
const [isScanning, setIsScanning] = useState(true);
const codeScanner = useCodeScanner({
codeTypes: ["qr", "ean-13", "code-128", "code-39"],
onCodeScanned: (codes) => {
if (isScanning && codes.length > 0) {
const scannedCode = codes;
setScannedData(scannedCode);
setIsScanning(false);
Alert.alert(
"Code Scanned",
`Type: ${scannedCode.type}
Value: ${scannedCode.value} `,
[
{
text: "Scan Again",
onPress: () => {
setScannedData(null);
setIsScanning(true);
}
}
]
);
}
}
});
if (!hasPermission || !device) {
return <View style={styles.container} />
}
return (
<View style={styles.container}>
<Camera
style={StyleSheet.absoluteFill}
device={device}
isActive={true}
codeScanner={codeScanner}
/>
<View style={styles.overlay}>
<View style={styles.scanArea}>
<View style={styles.scanFrame} />
</View>
<View style={styles.instructionsContainer}>
<Text style={styles.instructions}>
{isScanning ? "Point camera at QR code or barcode" : "Code detected!"}
</Text>
{scannedData && (
<TouchableOpacity
style={styles.scanAgainButton}
onPress={() => {
setScannedData(null);
setIsScanning(true);
}}
>
<Text style={styles.scanAgainText}>Scan Again</Text>
</TouchableOpacity>
)}
</View>
</View>
</View>
);
};
const styles = StyleSheet.create({
container: {
flex: 1,
},
overlay: {
...StyleSheet.absoluteFillObject,
justifyContent: "center",
alignItems: "center",
},
scanArea: {
width: 250,
height: 250,
justifyContent: "center",
alignItems: "center",
},
scanFrame: {
width: 200,
height: 200,
borderWidth: 2,
borderColor: "white",
borderRadius: 10,
backgroundColor: "transparent",
},
instructionsContainer: {
position: "absolute",
bottom: 100,
alignItems: "center",
},
instructions: {
color: "white",
fontSize: 16,
textAlign: "center",
backgroundColor: "rgba(0, 0, 0, 0.7)",
padding: 15,
borderRadius: 10,
marginBottom: 20,
},
scanAgainButton: {
backgroundColor: "#007AFF",
paddingHorizontal: 20,
paddingVertical: 12,
borderRadius: 8,
},
scanAgainText: {
color: "white",
fontSize: 16,
fontWeight: "bold",
},
});
Common Troubleshooting
Issue: Camera not showing on simulator
Solution: Use a physical device - camera functionality requires actual camera hardware.
Issue: Permission denied errors
Solution: Ensure permissions are properly configured in both iOS and Android manifests.
Issue: App crashes on frame processor
Solution: Install react-native-worklets-core and configure Babel plugin properly.
Issue: Poor performance on older devices
Solution: Reduce video resolution and frame rate for better performance.
Conclusion
React Native Vision Camera provides a comprehensive solution for all camera-related needs in React Native applications. From basic photo capture to advanced features like real-time barcode scanning and frame processing, this library offers the flexibility and performance needed for professional mobile applications.
The examples provided in this guide cover the most common use cases, but the library extensive API allows for much more customization and advanced implementations. Whether you are building a simple photo app or a complex computer vision application, React Native Vision Camera has the tools you need to succeed.