A React component that renders a camera preview for React Native apps with photo/video capture, barcode scanning, and cross-platform support.
—
Pending
Does it follow best practices?
Impact
Pending
No eval scenarios have been run
Pending
The risk profile of this skill
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.
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>
];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>
];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>;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[]>;
};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;
}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,
},
});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',
},
});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>
);
}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>
);
}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>
);
}