React Native course · Module 6: Photos, camera and audio

expo-camera - Advanced Camera Control

2 min read ·
In this lesson5

For full control over the camera, use expo-camera!

Installation

npx expo install expo-camera

Basic Camera View

import { CameraView, CameraType, useCameraPermissions } from 'expo-camera';
import { useState, useRef } from 'react';

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

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

  if (!permission.granted) {
    return (
      <View style={styles.container}>
        <Text>We need access to the camera</Text>
        <Pressable onPress={requestPermission}>
          <Text>Grant permissions</Text>
        </Pressable>
      </View>
    );
  }

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

  return (
    <View style={styles.container}>
      <CameraView
        ref={cameraRef}
        style={styles.camera}
        facing={facing}
      >
        <View style={styles.controls}>
          <Pressable onPress={toggleCameraFacing}>
            <Ionicons name="camera-reverse" size={32} color="white" />
          </Pressable>
        </View>
      </CameraView>
    </View>
  );
}

Taking Photos

function CameraWithCapture() {
  const cameraRef = useRef<CameraView>(null);
  const [photo, setPhoto] = useState<string | null>(null);

  const takePicture = async () => {
    if (cameraRef.current) {
      const photo = await cameraRef.current.takePictureAsync({
        quality: 0.8,
        base64: false,
        exif: true,
      });

      if (photo) {
        setPhoto(photo.uri);
      }
    }
  };

  if (photo) {
    return (
      <View style={styles.previewContainer}>
        <Image source={{ uri: photo }} style={styles.preview} />
        <View style={styles.previewButtons}>
          <Pressable onPress={() => setPhoto(null)}>
            <Text>Retake</Text>
          </Pressable>
          <Pressable onPress={() => savePhoto(photo)}>
            <Text>Save</Text>
          </Pressable>
        </View>
      </View>
    );
  }

  return (
    <CameraView ref={cameraRef} style={styles.camera}>
      <Pressable style={styles.captureButton} onPress={takePicture}>
        <View style={styles.captureInner} />
      </Pressable>
    </CameraView>
  );
}

QR Code Scanning

import { CameraView, useCameraPermissions, BarcodeScanningResult } from 'expo-camera';

function QRScanner() {
  const [permission, requestPermission] = useCameraPermissions();
  const [scanned, setScanned] = useState(false);

  const handleBarCodeScanned = (result: BarcodeScanningResult) => {
    if (scanned) return;

    setScanned(true);
    Alert.alert(
      'Code scanned!',
      result.data,
      [{ text: 'Scan again', onPress: () => setScanned(false) }]
    );
  };

  return (
    <CameraView
      style={styles.camera}
      barcodeScannerSettings={{
        barcodeTypes: ['qr', 'ean13', 'ean8'],
      }}
      onBarcodeScanned={handleBarCodeScanned}
    >
      <View style={styles.overlay}>
        <View style={styles.scanArea} />
        <Text style={styles.instruction}>Point the camera at a QR code</Text>
      </View>
    </CameraView>
  );
}

Flash

function CameraWithFlash() {
  const [flash, setFlash] = useState<'off' | 'on' | 'auto'>('off');

  const toggleFlash = () => {
    setFlash((current) => {
      if (current === 'off') return 'on';
      if (current === 'on') return 'auto';
      return 'off';
    });
  };

  return (
    <CameraView style={styles.camera} flash={flash}>
      <Pressable onPress={toggleFlash}>
        <Ionicons
          name={flash === 'off' ? 'flash-off' : flash === 'on' ? 'flash' : 'flash-outline'}
          size={28}
          color="white"
        />
      </Pressable>
    </CameraView>
  );
}

expo-camera gives you full control over the device camera!

Code for this lesson: App.tsx
import React, { useState } from 'react';
import { View, Text, Pressable, StyleSheet, Alert } from 'react-native';
import { CameraView, useCameraPermissions, BarcodeScanningResult } from 'expo-camera';

export default function App() {
  const [permission, requestPermission] = useCameraPermissions();
  const [scanned, setScanned] = useState(false);

  const handleBarCodeScanned = (result: BarcodeScanningResult) => {
    if (scanned) return;
    setScanned(true);
    Alert.alert('Code scanned!', result.data, [
      { text: 'Scan again', onPress: () => setScanned(false) },
    ]);
  };

  if (!permission?.granted) {
    return (
      <View style={styles.center}>
        <Text style={styles.text}>We need camera access</Text>
        <Pressable style={styles.button} onPress={requestPermission}>
          <Text style={styles.buttonText}>Grant permission</Text>
        </Pressable>
      </View>
    );
  }

  return (
    <CameraView
      style={styles.camera}
      barcodeScannerSettings={{ barcodeTypes: ['qr'] }}
      // TODO: connect the onBarcodeScanned prop to the handleBarCodeScanned function

    >
      <View style={styles.overlay}>
        <Text style={styles.instruction}>Point the camera at a QR code</Text>
      </View>
    </CameraView>
  );
}

const styles = StyleSheet.create({
  center: { flex: 1, justifyContent: 'center', alignItems: 'center', backgroundColor: '#1a5276' },
  camera: { flex: 1 },
  overlay: { flex: 1, justifyContent: 'flex-end', alignItems: 'center', paddingBottom: 60 },
  instruction: { color: '#fff', fontSize: 16, backgroundColor: 'rgba(0,0,0,0.5)', padding: 10, borderRadius: 8 },
  text: { color: '#fff', fontSize: 16, marginBottom: 16 },
  button: { backgroundColor: '#2980b9', padding: 12, borderRadius: 8 },
  buttonText: { color: '#fff', fontSize: 16 },
});

Docs entries used in this lesson

Spotted a mistake in this lesson?

Check yourself

Answer the questions from this lesson. Pick an answer to see right away whether it is correct.

  1. 1. Which component displays the camera preview in expo-camera?

  2. 2. What values does the 'facing' prop accept in CameraView?

Hands-on tasks in the game

  • Horizontal ordering

    Arrange taking a picture:

  • Code editor

    Complete the scanner code with onBarcodeScanned.

Useful articles