The Ultimate Guide to React Native Vision Camera (Setup + Examples)

04 October 2025



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


šŸ“ø Photo and Video Capture - High-quality image and video recording
šŸ‘ļø 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."
        }
      ]
    ]
  }
}
Then run:
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.