CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-expo-camera

A React component that renders a camera preview for React Native apps with photo/video capture, barcode scanning, and cross-platform support.

Pending
Quality

Pending

Does it follow best practices?

Impact

Pending

No eval scenarios have been run

SecuritybySnyk

Pending

The risk profile of this skill

Overview
Eval results
Files

permissions.mddocs/

Permissions Management

Expo Camera provides comprehensive permission management for camera and microphone access through React hooks and direct async functions. This ensures proper user consent and handles platform-specific permission requirements.

Capabilities

Camera Permissions Hook

React hook for managing camera permissions with automatic status monitoring.

/**
 * Hook for camera permission management
 * @param options Optional configuration for permission requests
 * @returns Tuple containing [status, requestPermission, getPermission]
 */
const useCameraPermissions: (
  options?: PermissionHookOptions<object>
) => [
  PermissionResponse | null,
  () => Promise<PermissionResponse>,
  () => Promise<PermissionResponse>
];

Microphone Permissions Hook

React hook for managing microphone permissions (required for video recording with audio).

/**
 * Hook for microphone permission management
 * @param options Optional configuration for permission requests
 * @returns Tuple containing [status, requestPermission, getPermission]
 */
const useMicrophonePermissions: (
  options?: PermissionHookOptions<object>
) => [
  PermissionResponse | null,
  () => Promise<PermissionResponse>,
  () => Promise<PermissionResponse>
];

Direct Permission Functions

Async functions for direct permission management without hooks.

/**
 * Check current camera permission status
 * @returns Promise resolving to current permission response
 */
function getCameraPermissionsAsync(): Promise<PermissionResponse>;

/**
 * Request camera permissions from the user
 * On iOS requires NSCameraUsageDescription in Info.plist
 * @returns Promise resolving to permission response after user interaction
 */
function requestCameraPermissionsAsync(): Promise<PermissionResponse>;

/**
 * Check current microphone permission status
 * @returns Promise resolving to current permission response
 */
function getMicrophonePermissionsAsync(): Promise<PermissionResponse>;

/**
 * Request microphone permissions from the user
 * On iOS requires NSMicrophoneUsageDescription in Info.plist
 * @returns Promise resolving to permission response after user interaction
 */
function requestMicrophonePermissionsAsync(): Promise<PermissionResponse>;

Legacy Camera Object

For backward compatibility, all permission functions are also available through the legacy Camera object.

/**
 * Legacy Camera object containing permission methods and scanFromURLAsync
 * @deprecated Use named exports instead
 */
const Camera = {
  /**
   * Check current camera permission status
   * @returns Promise resolving to current permission response
   */
  getCameraPermissionsAsync(): Promise<PermissionResponse>;
  
  /**
   * Request camera permissions from the user
   * @returns Promise resolving to permission response after user interaction
   */
  requestCameraPermissionsAsync(): Promise<PermissionResponse>;
  
  /**
   * Check current microphone permission status
   * @returns Promise resolving to current permission response
   */
  getMicrophonePermissionsAsync(): Promise<PermissionResponse>;
  
  /**
   * Request microphone permissions from the user
   * @returns Promise resolving to permission response after user interaction
   */
  requestMicrophonePermissionsAsync(): Promise<PermissionResponse>;
  
  /**
   * Scan barcodes from image URL
   * @param url URL of the image to scan
   * @param barcodeTypes Array of barcode types (defaults to ['qr'])
   * @returns Promise resolving to array of detected barcodes
   */
  scanFromURLAsync(url: string, barcodeTypes?: BarcodeType[]): Promise<BarcodeScanningResult[]>;
};

Permission Types

Core types for permission management.

interface PermissionResponse {
  /**
   * Current permission status
   */
  status: PermissionStatus;
  
  /**
   * When the permission expires (if applicable)
   */
  expires: PermissionExpiration;
  
  /**
   * Whether permission can be asked again
   */
  canAskAgain: boolean;
  
  /**
   * Whether permission has been granted
   */
  granted: boolean;
}

type PermissionStatus = 
  | 'undetermined'
  | 'granted' 
  | 'denied';

type PermissionExpiration = 'never' | number;

interface PermissionHookOptions<T> {
  /**
   * Request permission immediately when hook is called
   */
  request?: boolean;
  
  /**
   * Additional data to pass with permission request
   */
  data?: T;
}

Usage Examples

Basic Hook Usage

import React, { useEffect } from 'react';
import { View, Text, TouchableOpacity, StyleSheet } from 'react-native';
import { CameraView, useCameraPermissions } from 'expo-camera';

export function CameraWithPermissions() {
  const [permission, requestPermission] = useCameraPermissions();

  if (!permission) {
    // Camera permissions are still loading
    return (
      <View style={styles.container}>
        <Text>Loading permissions...</Text>
      </View>
    );
  }

  if (!permission.granted) {
    // Camera permissions are not granted yet
    return (
      <View style={styles.container}>
        <Text style={styles.message}>
          We need your permission to show the camera
        </Text>
        <TouchableOpacity style={styles.button} onPress={requestPermission}>
          <Text style={styles.buttonText}>Grant Permission</Text>
        </TouchableOpacity>
        {!permission.canAskAgain && (
          <Text style={styles.warning}>
            Please enable camera permissions in device settings
          </Text>
        )}
      </View>
    );
  }

  return (
    <View style={styles.container}>
      <CameraView style={styles.camera} facing="back" />
    </View>
  );
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    justifyContent: 'center',
  },
  message: {
    textAlign: 'center',
    paddingBottom: 10,
  },
  camera: {
    flex: 1,
  },
  button: {
    padding: 10,
    backgroundColor: '#007AFF',
    margin: 20,
    borderRadius: 5,
  },
  buttonText: {
    color: 'white',
    textAlign: 'center',
  },
  warning: {
    textAlign: 'center',
    color: 'red',
    fontSize: 12,
  },
});

Video Recording with Microphone Permissions

import React, { useState, useRef } from 'react';
import { View, TouchableOpacity, Text, Alert, StyleSheet } from 'react-native';
import { 
  CameraView, 
  useCameraPermissions, 
  useMicrophonePermissions,
  CameraRecordingOptions 
} from 'expo-camera';

export function VideoRecordingCamera() {
  const [isRecording, setIsRecording] = useState(false);
  const [cameraPermission, requestCameraPermission] = useCameraPermissions();
  const [microphonePermission, requestMicrophonePermission] = useMicrophonePermissions();
  const cameraRef = useRef<CameraView>(null);

  const checkPermissions = async () => {
    if (!cameraPermission?.granted) {
      const result = await requestCameraPermission();
      if (!result.granted) {
        Alert.alert('Camera permission is required');
        return false;
      }
    }

    if (!microphonePermission?.granted) {
      const result = await requestMicrophonePermission();
      if (!result.granted) {
        Alert.alert('Microphone permission is required for video with audio');
        return false;
      }
    }

    return true;
  };

  const startRecording = async () => {
    if (!cameraRef.current || !(await checkPermissions())) return;

    try {
      setIsRecording(true);
      const recordingOptions: CameraRecordingOptions = {
        maxDuration: 60, // 60 seconds max
        maxFileSize: 100 * 1024 * 1024, // 100MB max
      };
      
      const video = await cameraRef.current.record(recordingOptions);
      console.log('Video recorded:', video.uri);
    } catch (error) {
      Alert.alert('Recording failed', error.message);
    } finally {
      setIsRecording(false);
    }
  };

  const stopRecording = async () => {
    if (cameraRef.current && isRecording) {
      await cameraRef.current.stopRecording();
      setIsRecording(false);
    }
  };

  if (!cameraPermission || !microphonePermission) {
    return (
      <View style={styles.container}>
        <Text>Loading permissions...</Text>
      </View>
    );
  }

  return (
    <View style={styles.container}>
      <CameraView
        ref={cameraRef}
        style={styles.camera}
        facing="back"
        mode="video"
      />
      <View style={styles.controls}>
        <TouchableOpacity
          style={[styles.button, isRecording && styles.recordingButton]}
          onPress={isRecording ? stopRecording : startRecording}
        >
          <Text style={styles.buttonText}>
            {isRecording ? 'Stop Recording' : 'Start Recording'}
          </Text>
        </TouchableOpacity>
      </View>
    </View>
  );
}

const styles = StyleSheet.create({
  container: { flex: 1 },
  camera: { flex: 1 },
  controls: {
    position: 'absolute',
    bottom: 50,
    left: 0,
    right: 0,
    alignItems: 'center',
  },
  button: {
    backgroundColor: '#007AFF',
    padding: 15,
    borderRadius: 25,
  },
  recordingButton: {
    backgroundColor: '#FF3B30',
  },
  buttonText: {
    color: 'white',
    fontWeight: 'bold',
  },
});

Direct Permission Functions

import React, { useEffect, useState } from 'react';
import { View, Text, TouchableOpacity } from 'react-native';
import { 
  getCameraPermissionsAsync, 
  requestCameraPermissionsAsync,
  PermissionResponse 
} from 'expo-camera';

export function DirectPermissionExample() {
  const [cameraPermission, setCameraPermission] = useState<PermissionResponse | null>(null);

  useEffect(() => {
    checkCameraPermissions();
  }, []);

  const checkCameraPermissions = async () => {
    try {
      const permission = await getCameraPermissionsAsync();
      setCameraPermission(permission);
    } catch (error) {
      console.error('Failed to check camera permissions:', error);
    }
  };

  const requestCameraPermissions = async () => {
    try {
      const permission = await requestCameraPermissionsAsync();
      setCameraPermission(permission);
      
      if (permission.granted) {
        console.log('Camera permission granted');
      } else if (!permission.canAskAgain) {
        console.log('User denied permission permanently');
      } else {
        console.log('User denied permission');
      }
    } catch (error) {
      console.error('Failed to request camera permissions:', error);
    }
  };

  return (
    <View style={{ flex: 1, justifyContent: 'center', padding: 20 }}>
      <Text style={{ marginBottom: 20 }}>
        Camera Permission Status: {cameraPermission?.status || 'Unknown'}
      </Text>
      <Text style={{ marginBottom: 20 }}>
        Granted: {cameraPermission?.granted ? 'Yes' : 'No'}
      </Text>
      <Text style={{ marginBottom: 20 }}>
        Can Ask Again: {cameraPermission?.canAskAgain ? 'Yes' : 'No'}
      </Text>
      
      {!cameraPermission?.granted && (
        <TouchableOpacity
          style={{
            backgroundColor: '#007AFF',
            padding: 15,
            borderRadius: 5,
            alignItems: 'center',
          }}
          onPress={requestCameraPermissions}
        >
          <Text style={{ color: 'white' }}>Request Camera Permission</Text>
        </TouchableOpacity>
      )}
    </View>
  );
}

Permission Status Monitoring

import React, { useState, useEffect } from 'react';
import { View, Text } from 'react-native';
import { 
  useCameraPermissions, 
  useMicrophonePermissions,
  PermissionResponse 
} from 'expo-camera';

export function PermissionMonitor() {
  const [cameraPermission, requestCameraPermission, getCameraPermission] = useCameraPermissions();
  const [microphonePermission, requestMicrophonePermission, getMicrophonePermission] = useMicrophonePermissions();

  // Monitor permission changes
  useEffect(() => {
    const intervalId = setInterval(async () => {
      // Manually refresh permissions if needed
      await getCameraPermission();
      await getMicrophonePermission();
    }, 5000); // Check every 5 seconds

    return () => clearInterval(intervalId);
  }, [getCameraPermission, getMicrophonePermission]);

  const renderPermissionStatus = (permission: PermissionResponse | null, name: string) => (
    <View style={{ marginBottom: 20 }}>
      <Text style={{ fontWeight: 'bold' }}>{name} Permission:</Text>
      <Text>Status: {permission?.status || 'Loading...'}</Text>
      <Text>Granted: {permission?.granted ? 'Yes' : 'No'}</Text>
      <Text>Can Ask Again: {permission?.canAskAgain ? 'Yes' : 'No'}</Text>
      <Text>Expires: {permission?.expires === 'never' ? 'Never' : permission?.expires}</Text>
    </View>
  );

  return (
    <View style={{ flex: 1, padding: 20 }}>
      <Text style={{ fontSize: 18, fontWeight: 'bold', marginBottom: 20 }}>
        Permission Status Monitor
      </Text>
      
      {renderPermissionStatus(cameraPermission, 'Camera')}
      {renderPermissionStatus(microphonePermission, 'Microphone')}
    </View>
  );
}

Error Handling

import React from 'react';
import { View, Text, TouchableOpacity, Alert } from 'react-native';
import { useCameraPermissions } from 'expo-camera';

export function PermissionErrorHandling() {
  const [permission, requestPermission] = useCameraPermissions();

  const handlePermissionRequest = async () => {
    try {
      const result = await requestPermission();
      
      if (result.granted) {
        Alert.alert('Success', 'Camera permission granted');
      } else if (!result.canAskAgain) {
        Alert.alert(
          'Permission Denied',
          'Camera permission was permanently denied. Please enable it in device settings.',
          [
            { text: 'Cancel', style: 'cancel' },
            { text: 'Open Settings', onPress: () => {
              // Open device settings - platform specific implementation needed
              console.log('Would open device settings');
            }}
          ]
        );
      } else {
        Alert.alert('Permission Required', 'Camera access is needed to take photos');
      }
    } catch (error) {
      Alert.alert('Error', `Failed to request permission: ${error.message}`);
    }
  };

  return (
    <View style={{ flex: 1, justifyContent: 'center', padding: 20 }}>
      {permission?.granted ? (
        <Text style={{ textAlign: 'center', fontSize: 18 }}>
          Camera permission is granted! 📸
        </Text>
      ) : (
        <TouchableOpacity
          style={{
            backgroundColor: '#007AFF',
            padding: 15,
            borderRadius: 5,
            alignItems: 'center',
          }}
          onPress={handlePermissionRequest}
        >
          <Text style={{ color: 'white' }}>Request Camera Permission</Text>
        </TouchableOpacity>
      )}
    </View>
  );
}

docs

barcode-scanning.md

camera-component.md

index.md

media-capture.md

permissions.md

tile.json