Handling Device Hardware: Camera, GPS Location, and Sensors in React Native.

Handling Device Hardware: Camera, GPS Location, and Sensors in React Native | 2026 Guide

Handling Device Hardware: Camera, GPS Location, and Sensors in React Native

React Native Hardware Access in 2026

Mobile apps in 2026 are expected to do more than display content — they must interact with the physical world. Whether it's a fitness tracker reading accelerometer data, a delivery app tracking GPS coordinates, or a social platform applying real-time filters to camera frames, hardware access is no longer a premium feature. It's table stakes.

React Native has matured significantly in this area. The New Architecture (Fabric + TurboModules + JSI) has eliminated the JavaScript bridge bottleneck, enabling frame-by-frame camera processing at 60 FPS with zero overhead. Expo's module ecosystem has grown to over 50 native APIs, and specialized libraries like react-native-vision-camera have become the standard for advanced computer vision tasks.

This guide covers the three pillars of React Native hardware access in 2026: camera integration, GPS location tracking, and device sensors. You'll get production-ready code, performance benchmarks, and the decision framework for choosing between Expo's managed workflow and bare React Native CLI.

60 FPS VisionCamera Frame Processing
~3m GPS Accuracy (High Precision)
50+ Expo Native API Modules
7K+ VisionCamera GitHub Stars
AdSense Display Ad — 728x90 / Responsive

Camera Integration: Choosing the Right Library

Camera integration in React Native is not one-size-fits-all. The ecosystem has stratified into three distinct layers, each serving different use cases:

  • Expo ImagePicker: Uses the native OS image picker for gallery selection or quick photo capture — no custom UI needed
  • Expo Camera: Full custom camera preview with photo/video capture, built into the Expo ecosystem, works in Expo Go
  • react-native-vision-camera: High-performance camera with frame processors running on the GPU thread — real-time ML inference, QR scanning, and computer vision
Feature Expo ImagePicker Expo Camera VisionCamera
Native OS Picker Yes No No
Custom Camera UI No Yes Yes
Frame Processors (Real-time ML) No No Yes (GPU thread)
QR/Barcode Scanning No Basic Advanced
4K/HDR Video OS-dependent No Yes
Works in Expo Go Yes Yes No (requires prebuild)
New Architecture Required No No Yes (for frame processors)
Setup Complexity Very Low Low High

Expo Camera: Standard Photo & Video Capture

Expo Managed Workflow

Expo Camera is the go-to solution for 80% of camera use cases in React Native. It provides a CameraView component that renders a live camera preview with full control over facing direction, flash, zoom, and capture quality. Best of all, it works inside Expo Go — no native code changes or prebuild required.

Installation & Setup

Terminal
npx expo install expo-camera

Basic Camera Screen Implementation

TypeScript — Expo Camera
import { CameraView, CameraType, useCameraPermissions } from 'expo-camera';
import { useState, useRef } from 'react';
import { Button, StyleSheet, Text, TouchableOpacity, View } from 'react-native';

export function CameraScreen() {
  const [facing, setFacing] = useState<CameraType>('back');
  const [permission, requestPermission] = useCameraPermissions();
  const cameraRef = useRef<CameraView>(null);
  const [photo, setPhoto] = useState<string | null>(null);

  if (!permission) {
    return <View />; // Loading permissions
  }

  if (!permission.granted) {
    return (
      <View style={styles.container}>
        <Text>Camera permission is required.</Text>
        <Button onPress={requestPermission} title="Grant Permission" />
      </View>
    );
  }

  const takePhoto = async () => {
    if (!cameraRef.current) return;
    const pic = await cameraRef.current.takePictureAsync({
      quality: 0.9,
      base64: false,
      skipProcessing: false,
    });
    if (pic) setPhoto(pic.uri);
  };

  const toggleFacing = () => {
    setFacing((current) => (current === 'back' ? 'front' : 'back'));
  };

  return (
    <View style={styles.container}>
      <CameraView style={styles.camera} facing={facing} ref={cameraRef}>
        <View style={styles.buttonContainer}>
          <TouchableOpacity onPress={toggleFacing} style={styles.button}>
            <Text style={styles.text}>Flip</Text>
          </TouchableOpacity>
          <TouchableOpacity onPress={takePhoto} style={styles.shutterButton} />
        </View>
      </CameraView>
    </View>
  );
}

📸 Expo Camera Best For:

  • Profile picture uploads and photo galleries
  • Basic QR/barcode scanning within Expo managed workflow
  • Photo and video capture with custom UI controls
  • Rapid prototyping without native code changes
  • Teams that prioritize Expo Go for development speed

Video Recording with Expo Camera

TypeScript — Video Recording
const [isRecording, setIsRecording] = useState(false);

const startRecording = async () => {
  if (!cameraRef.current) return;
  setIsRecording(true);
  
  const video = await cameraRef.current.recordAsync({
    maxDuration: 60, // seconds
    maxFileSize: 50 * 1024 * 1024, // 50MB
    mute: false,
  });
  
  setIsRecording(false);
  console.log('Video saved:', video.uri);
};

const stopRecording = () => {
  cameraRef.current?.stopRecording();
};
AdSense In-Article Ad — 336x280 / Responsive

VisionCamera: Advanced Camera with Frame Processing

Requires New Architecture / Bare Workflow

react-native-vision-camera is the high-performance camera library for React Native. Its frame processors run on the GPU thread with zero JavaScript bridge overhead, enabling real-time computer vision at 60 FPS. This is the library powering QR scanning, face detection, pose estimation, and on-device ML inference directly from the live camera feed.

Key Capabilities

  • Frame Processors: Run on GPU thread — analyze every frame in real-time without blocking the UI
  • Real-Time ML: Integrate TensorFlow Lite, ML Kit, or custom models for live inference
  • Format Control: Select exact resolution (up to 4K/8K), frame rate (120fps/240fps slow motion), and codec (H.264/H.265)
  • Advanced Capture: RAW capture, HDR, night mode, and custom color spaces

Important: VisionCamera requires the New Architecture (JSI/Fabric) and does NOT work in Expo Go. You must use expo prebuild (bare workflow) or React Native CLI. Budget 2–3 days for initial setup and migration if coming from Expo Camera.

Installation & Setup

Terminal
# Requires New Architecture — ensure it's enabled in your project
npm install react-native-vision-camera

# iOS
cd ios && pod install

# Android — no additional steps needed

Basic Camera with Frame Processor

TypeScript — VisionCamera with QR Scanning
import { Camera, useCameraDevice, useCameraPermission, useFrameProcessor } from 'react-native-vision-camera';
import { useState } from 'react';
import { runAtTargetFps } from 'react-native-vision-camera';
import { scanCodes, CodeType } from 'vision-camera-code-scanner';

export function QRScannerScreen() {
  const device = useCameraDevice('back');
  const { hasPermission, requestPermission } = useCameraPermission();
  const [scannedCode, setScannedCode] = useState<string | null>(null);

  const frameProcessor = useFrameProcessor((frame) => {
    'worklet'; // Runs on UI thread, not JS thread
    
    runAtTargetFps(5, () => {
      'worklet';
      const detectedCodes = scanCodes(frame, [CodeType.QR]);
      if (detectedCodes.length > 0) {
        // Use runOnJS to update React state from worklet
        runOnJS(setScannedCode)(detectedCodes[0].value);
      }
    });
  }, []);

  if (!hasPermission) {
    return (
      <View>
        <Text>Camera permission required</Text>
        <Button onPress={requestPermission} title="Grant" />
      </View>
    );
  }

  if (device == null) return <Text>No camera device</Text>;

  return (
    <View style={{ flex: 1 }}>
      <Camera
        style={StyleSheet.absoluteFill}
        device={device}
        isActive={true}
        frameProcessor={frameProcessor}
        pixelFormat="yuv"
      />
      {scannedCode && (
        <View style={styles.overlay}>
          <Text>Scanned: {scannedCode}</Text>
        </View>
      )}
    </View>
  );
}

🎯 VisionCamera Best For:

  • Real-time QR/barcode scanning at high speed
  • On-device ML inference (face detection, pose estimation, OCR)
  • AR overlays and computer vision applications
  • Professional video recording with format control (4K, HDR, slow-mo)
  • Apps where camera performance is a competitive differentiator

Frame Processor Performance Tips

TypeScript — Performance Optimization
const frameProcessor = useFrameProcessor((frame) => {
  'worklet';
  
  // ✅ GOOD: Limit processing frequency
  runAtTargetFps(5, () => {
    // Process only 5 frames per second, not 60
    const codes = scanCodes(frame, [CodeType.QR]);
  });
  
  // ✅ GOOD: Downscale frame for faster processing
  const resized = resize(frame, {
    scale: { width: 320, height: 240 },
    pixelFormat: 'rgb',
  });
  
  // ❌ BAD: Heavy JS computation in frame processor
  // Never do: fetch(), setState(), or complex JS operations
}, []);

GPS Location Tracking: Precision & Battery Optimization

Location tracking is where React Native apps often fail — not because the APIs are hard, but because developers don't understand the trade-offs between accuracy, battery life, and user privacy. In 2026, both iOS and Android have tightened location permission requirements, making proper implementation more critical than ever.

Expo Location: The Standard Approach

Expo Location provides a unified API for foreground and background location, geofencing, and reverse geocoding. It handles platform differences automatically and is the recommended starting point for most apps.

Terminal
npx expo install expo-location

Foreground Location with Permission Handling

TypeScript — Foreground Location
import * as Location from 'expo-location';
import { useState, useEffect } from 'react';

export function useLocation() {
  const [location, setLocation] = useState<Location.LocationObject | null>(null);
  const [errorMsg, setErrorMsg] = useState<string | null>(null);

  useEffect(() => {
    (async () => {
      // Request foreground permission
      let { status } = await Location.requestForegroundPermissionsAsync();
      if (status !== 'granted') {
        setErrorMsg('Permission to access location was denied');
        return;
      }

      // Get current position with high accuracy
      let currentLocation = await Location.getCurrentPositionAsync({
        accuracy: Location.Accuracy.High, // ~3m accuracy, higher battery usage
      });
      
      setLocation(currentLocation);
    })();
  }, []);

  return { location, errorMsg };
}

// Usage in component
export function LocationDisplay() {
  const { location, errorMsg } = useLocation();

  if (errorMsg) return <Text>{errorMsg}</Text>;
  if (!location) return <Text>Loading location...</Text>;

  return (
    <View>
      <Text>Latitude: {location.coords.latitude}</Text>
      <Text>Longitude: {location.coords.longitude}</Text>
      <Text>Accuracy: {location.coords.accuracy}m</Text>
      <Text>Altitude: {location.coords.altitude}m</Text>
    </View>
  );
}

Background Location Tracking

Background location requires additional permissions and careful battery management. iOS 17+ and Android 14+ have strict requirements for background location access.

TypeScript — Background Location
import * as Location from 'expo-location';
import * as TaskManager from 'expo-task-manager';

const LOCATION_TASK_NAME = 'background-location-task';

// Define the background task
TaskManager.defineTask(LOCATION_TASK_NAME, ({ data, error }) => {
  if (error) {
    console.error('Location task error:', error);
    return;
  }
  
  if (data) {
    const { locations } = data as { locations: Location.LocationObject[] };
    const { latitude, longitude } = locations[0].coords;
    
    // Send to your backend or store locally
    console.log('Background location:', latitude, longitude);
    
    // ⚠️ iOS limits background execution time — batch and sync efficiently
  }
});

export async function startBackgroundTracking() {
  // Request background permission (separate from foreground)
  const { status: backgroundStatus } = await Location.requestBackgroundPermissionsAsync();
  if (backgroundStatus !== 'granted') {
    console.log('Background location permission denied');
    return;
  }

  await Location.startLocationUpdatesAsync(LOCATION_TASK_NAME, {
    accuracy: Location.Accuracy.Balanced, // Balanced accuracy for battery
    timeInterval: 10000, // Update every 10 seconds
    distanceInterval: 10, // Or every 10 meters
    foregroundService: {
      notificationTitle: 'Tracking your route',
      notificationBody: 'Location is being used in background',
    },
    // iOS-specific
    pausesUpdatesAutomatically: true, // Pause when not moving
    activityType: Location.ActivityType.Fitness, // Optimize for fitness tracking
  });
}

Geofencing: Enter/Exit Region Monitoring

TypeScript — Geofencing
import * as Location from 'expo-location';

export async function setupGeofencing() {
  await Location.startGeofencingAsync('geofence-task', [
    {
      identifier: 'office',
      latitude: 40.7128,
      longitude: -74.0060,
      radius: 100, // meters
      notifyOnEnter: true,
      notifyOnExit: true,
    },
    {
      identifier: 'gym',
      latitude: 40.7580,
      longitude: -73.9855,
      radius: 50,
      notifyOnEnter: true,
      notifyOnExit: false,
    },
  ]);
}

TaskManager.defineTask('geofence-task', ({ data, error }) => {
  if (error) return;
  
  const { eventType, region } = data as {
    eventType: Location.GeofencingEventType;
    region: Location.LocationRegion;
  };
  
  if (eventType === Location.GeofencingEventType.Enter) {
    console.log(`Entered ${region.identifier}`);
    // Trigger local notification or update UI
  } else {
    console.log(`Exited ${region.identifier}`);
  }
});
AdSense In-Article Ad — 336x280 / Responsive

Device Sensors: Accelerometer, Gyroscope, Magnetometer

React Native provides access to the full suite of device sensors through Expo's sensor modules. These enable fitness tracking, gaming controls, step counting, compass functionality, and motion-based UI interactions.

Available Sensors in 2026

Sensor Module Use Case Update Rate
Accelerometer expo-sensors Shake detection, step counting, tilt controls Up to 100Hz
Gyroscope expo-sensors Rotation tracking, gaming, VR/AR Up to 100Hz
Magnetometer expo-sensors Compass, metal detection Up to 50Hz
Barometer expo-sensors Altitude estimation, weather ~1Hz
Pedometer expo-sensors Step counting, fitness tracking Event-driven
Light Sensor expo-sensors Auto-brightness, adaptive UI ~5Hz

Accelerometer: Shake Detection

TypeScript — Shake Detection
import { Accelerometer } from 'expo-sensors';
import { useState, useEffect } from 'react';

const SHAKE_THRESHOLD = 1.5; // G-force

export function useShakeDetection(onShake: () => void) {
  const [subscription, setSubscription] = useState<any>(null);

  useEffect(() => {
    const subscribe = () => {
      setSubscription(
        Accelerometer.addListener((accelerometerData) => {
          const { x, y, z } = accelerometerData;
          const magnitude = Math.sqrt(x * x + y * y + z * z);
          
          if (magnitude > SHAKE_THRESHOLD) {
            onShake();
          }
        })
      );
    };

    // Set update interval (default is 100ms = 10Hz)
    Accelerometer.setUpdateInterval(100);
    subscribe();

    return () => {
      subscription && subscription.remove();
      setSubscription(null);
    };
  }, [onShake]);

  return subscription;
}

// Usage
export function ShakeToRefresh() {
  const [lastShake, setLastShake] = useState<Date>(new Date());
  
  useShakeDetection(() => {
    setLastShake(new Date());
    // Trigger refresh logic
  });

  return <Text>Last shake: {lastShake.toLocaleTimeString()}</Text>;
}

Gyroscope: Rotation Tracking

TypeScript — Gyroscope Rotation
import { Gyroscope } from 'expo-sensors';
import { useState, useEffect } from 'react';
import { Animated } from 'react-native';

export function GyroscopeRotation() {
  const [rotation] = useState(new Animated.ValueXY({ x: 0, y: 0 }));
  
  useEffect(() => {
    Gyroscope.setUpdateInterval(16); // ~60Hz for smooth animation
    
    const subscription = Gyroscope.addListener((data) => {
      // Map gyroscope data to rotation angles
      // x = pitch, y = roll, z = yaw
      rotation.setValue({
        x: data.y * 50, // Scale factor for visual effect
        y: data.x * 50,
      });
    });

    return () => subscription.remove();
  }, []);

  const rotateX = rotation.x.interpolate({
    inputRange: [-100, 100],
    outputRange: ['-30deg', '30deg'],
  });

  const rotateY = rotation.y.interpolate({
    inputRange: [-100, 100],
    outputRange: ['30deg', '-30deg'],
  });

  return (
    <Animated.View
      style={[
        styles.box,
        {
          transform: [
            { rotateX },
            { rotateY },
          ],
        },
      ]}
    />
  );
}

Pedometer: Step Counting

TypeScript — Step Counter
import { Pedometer } from 'expo-sensors';
import { useState, useEffect } from 'react';

export function usePedometer() {
  const [isAvailable, setIsAvailable] = useState(false);
  const [stepCount, setStepCount] = useState(0);
  const [pastSteps, setPastSteps] = useState(0);

  useEffect(() => {
    const subscribe = async () => {
      const isStepCountingAvailable = await Pedometer.isAvailableAsync();
      setIsAvailable(isStepCountingAvailable);

      if (isStepCountingAvailable) {
        // Get steps from midnight today
        const start = new Date();
        start.setHours(0, 0, 0, 0);
        
        const result = await Pedometer.getStepCountAsync(start, new Date());
        setPastSteps(result.steps);

        // Subscribe to real-time updates
        return Pedometer.watchStepCount((result) => {
          setStepCount(result.steps);
        });
      }
    };

    const subscription = subscribe();
    return () => {
      subscription?.then(sub => sub?.remove());
    };
  }, []);

  return { isAvailable, todaySteps: pastSteps + stepCount };
}

Permission Handling: iOS & Android Best Practices

Both iOS and Android have tightened permission requirements significantly. iOS 17+ requires runtime permission dialogs with clear usage descriptions, and Android 14+ mandates foreground service types for background location. Getting this wrong means app store rejection or crashes.

iOS Permission Configuration

XML — ios/YourApp/Info.plist
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
  <!-- Camera -->
  <key>NSCameraUsageDescription</key>
  <string>This app uses the camera to scan QR codes and take photos.</string>
  
  <!-- Location -->
  <key>NSLocationWhenInUseUsageDescription</key>
  <string>Your location is used to show nearby restaurants and track delivery progress.</string>
  
  <key>NSLocationAlwaysAndWhenInUseUsageDescription</key>
  <string>Background location is used to track your running route even when the app is closed.</string>
  
  <!-- Motion Sensors -->
  <key>NSMotionUsageDescription</key>
  <string>Motion data is used to count your daily steps and detect shake gestures.</string>
  
  <!-- Photo Library -->
  <key>NSPhotoLibraryUsageDescription</key>
  <string>Photos are used to set your profile picture and share images with friends.</string>
</dict>
</plist>

Android Permission Configuration

XML — android/app/src/main/AndroidManifest.xml
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
  
  <!-- Camera -->
  <uses-permission android:name="android.permission.CAMERA" />
  
  <!-- Location -->
  <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
  <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
  <uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />
  
  <!-- Foreground service for background location (Android 14+) -->
  <uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
  <uses-permission android:name="android.permission.FOREGROUND_SERVICE_LOCATION" />
  
  <!-- Sensors -->
  <uses-permission android:name="android.permission.ACTIVITY_RECOGNITION" />
  <uses-permission android:name="android.permission.HIGH_SAMPLING_RATE_SENSORS" />
  
  <!-- Hardware features -->
  <uses-feature android:name="android.hardware.camera" android:required="false" />
  <uses-feature android:name="android.hardware.location.gps" android:required="false" />
  <uses-feature android:name="android.hardware.sensor.accelerometer" android:required="false" />
  
</manifest>

Permission Best Practice: Always request permissions in context — when the user taps "Take Photo," not at app launch. Explain why you need the permission before showing the system dialog. iOS apps that request permissions at launch without context face higher rejection rates.

Performance Optimization for Hardware Access

Hardware access is the fastest way to drain battery and degrade app performance. These are the non-negotiable practices for production apps.

Camera Performance

  • Release Camera When Backgrounded: Always pause the camera preview when the app enters background. iOS will terminate apps that hold the camera in background.
  • Use Appropriate Resolution: Don't capture 4K photos for profile pictures. Set quality: 0.7 and appropriate dimensions.
  • Compress Before Upload: Use ImageManipulator from Expo to resize and compress images before network upload.
  • Frame Processor Throttling: Limit frame processors to 5–10 FPS for analysis tasks. Processing every frame at 60 FPS drains battery in minutes.

Location Performance

  • Use Significant Location Changes: For apps that don't need constant tracking, iOS's significant location change API wakes your app only when the user moves ~500 meters.
  • Batch Updates: Collect 10–20 location points before syncing to your server. Each network request wakes the radio chip, which is expensive.
  • Stop When Not Needed: Automatically stop location tracking when the user reaches their destination or after a timeout.

Sensor Performance

  • Reduce Update Frequency: 10Hz (100ms interval) is sufficient for most use cases. 100Hz drains battery 10x faster with minimal UX benefit.
  • Unsubscribe on Unmount: Always remove sensor listeners when components unmount. Orphaned listeners keep sensors active and drain battery.
  • Use Hardware Step Counter: On Android, prefer hardwareStepCounter over hardwareAccelerometer for step counting — it's more accurate and battery-efficient.

Testing Hardware Features on Simulators & Real Devices

Hardware features cannot be fully tested on simulators. Here's the testing strategy that catches 99% of production issues.

Feature Simulator Testing Real Device Testing Automated Testing
Camera Basic preview (macOS camera) Required — focus, flash, performance Detox + mock camera module
Location Simulated coordinates Required — GPS accuracy, background behavior Mock location provider
Sensors Not available Required — all sensor data Mock sensor data injection
Permissions System dialogs Required — first-time user flow XCUITest / Espresso
Recommended

📱 Ship Hardware-First Apps with Confidence

Need help integrating camera, GPS, or sensors into your React Native app? Our team has shipped 50+ production apps with advanced hardware features. Get a code review, architecture assessment, or full implementation support for your next project.

Book a Free Consultation

Conclusion: Building Hardware-First Mobile Experiences

React Native in 2026 is no longer limited to simple UI apps. The New Architecture, Expo's 50+ native modules, and specialized libraries like VisionCamera have transformed it into a platform capable of handling the most demanding hardware use cases — real-time computer vision, precision GPS tracking, and high-frequency sensor data.

The key to success is not choosing the most powerful library, but the right library for your use case. Expo Camera covers 80% of camera needs without leaving the managed workflow. VisionCamera is there when you need frame-by-frame analysis. Expo Location provides everything for GPS tracking, and expo-sensors unlocks the full suite of device sensors with a unified API.

Performance and battery optimization are not afterthoughts — they're requirements. Throttle frame processors, batch location updates, reduce sensor frequency, and always unsubscribe listeners. Users will abandon apps that drain their battery, regardless of how impressive the features are.

The hardware is there. The APIs are mature. The only question is what you'll build with them.

"The best React Native apps in 2026 don't treat hardware access as a feature — they treat it as the foundation. Camera, location, and sensors are not add-ons; they're the reason users choose mobile over web."

Your Next Step: Pick one hardware feature your app needs but doesn't have yet. Implement it this week using the code examples in this guide. Start with Expo Camera or Expo Location — they're the fastest path from idea to working prototype.

Key technical paths

Choose your major
ads here