expo-camera & expo-image-picker
Flujos de captura, permisos y vistas previas seguras en memoria - el cookbook de cámara para apps de Expo SDK 57 que adjuntan fotos a formularios, perfiles e informes de campo.
Busca en todas las páginas de la documentación
Flujos de captura, permisos y vistas previas seguras en memoria - el cookbook de cámara para apps de Expo SDK 57 que adjuntan fotos a formularios, perfiles e informes de campo.
Tarjeta de referencia rápida - lista para copiar y pegar.
npx expo install expo-camera expo-image-picker// app.config.ts
export default {
plugins: [
[
"expo-camera",
{
cameraPermission:
"Allow InspectPro to photograph equipment for work orders.",
microphonePermission:
"Allow InspectPro to record audio with video inspections.",
recordAudioAndroid: true,
},
],
[
"expo-image-picker",
{
photosPermission:
"Allow InspectPro to attach existing photos from your library.",
},
],
],
};import * as ImagePicker from "expo-image-picker";
export async function pickImageFromLibrary() {
const { status } = await ImagePicker.requestMediaLibraryPermissionsAsync();
if (status !== "granted") return null;
const result = await ImagePicker.launchImageLibraryAsync({
mediaTypes: ["images"],
allowsEditing: true,
quality: 0.7,
exif: false,
});
if (result.canceled) return null;
return result.assets[0];
}Cuándo usarlo:
expo-camera (módulo BarcodeScanner).Cuándo evitarlo:
Vista previa de cámara con puerta de permiso, fallback de biblioteca y URI de vista previa reducida.
npx expo install expo-camera expo-image-picker expo-image-manipulator// src/media/CaptureSheet.tsx
import { useRef, useState } from "react";
import { Image, Pressable, Text, View } from "react-native";
import { CameraView, useCameraPermissions } from "expo-camera";
import * as ImageManipulator from "expo-image-manipulator";
import * as ImagePicker from "expo-image-picker";
async function pickImageFromLibrary() {
const { status } = await ImagePicker.requestMediaLibraryPermissionsAsync();
if (status !== "granted") return null;
const result = await ImagePicker.launchImageLibraryAsync({
mediaTypes: ["images"],
allowsEditing: true,
quality: 0.7,
});
if (result.canceled) return null;
return result.assets[0];
}
type Props = {
onCaptured: (uri: string) => void;
};
export function CaptureSheet({ onCaptured }: Props) {
const cameraRef = useRef<CameraView>(null);
const [permission, requestPermission] = useCameraPermissions();
const [previewUri, setPreviewUri] = useState<string | null>(null);
if (!permission?.granted) {
return (
<View style={{ padding: 24, gap: 12 }}>
<Text>Photograph equipment labels for audit trails.</Text>
<Pressable onPress={requestPermission}>
<Text style={{ color: "#2563eb" }}>Allow camera</Text>
</Pressable>
<Pressable
onPress={async () => {
const asset = await pickImageFromLibrary();
if (asset) await setDownscaledPreview(asset.uri);
}}
>
<Text>Choose from library instead</Text>
</Pressable>
</View>
);
}
async function setDownscaledPreview(uri: string) {
const manipulated = await ImageManipulator.manipulateAsync(
uri,
[{ resize: { width: 1280 } }],
{ compress: 0.75, format: ImageManipulator.SaveFormat.JPEG }
);
setPreviewUri(manipulated.uri);
}
async function takePhoto() {
const photo = await cameraRef.current?.takePictureAsync({
quality: 0.8,
skipProcessing: false,
});
if (photo?.uri) await setDownscaledPreview(photo.uri);
}
return (
<View style={{ flex: 1 }}>
{previewUri ? (
<View style={{ flex: 1, gap: 12, padding: 16 }}>
<Image
source={{ uri: previewUri }}
style={{ flex: 1, borderRadius: 8 }}
resizeMode="contain"
/>
<Pressable onPress={() => onCaptured(previewUri)}>
<Text style={{ fontWeight: "600" }}>Use photo</Text>
</Pressable>
<Pressable onPress={() => setPreviewUri(null)}>
<Text>Retake</Text>
</Pressable>
</View>
) : (
<CameraView ref={cameraRef} style={{ flex: 1 }} facing="back">
<Pressable onPress={takePhoto} style={{ margin: 24, padding: 16 }}>
<Text style={{ color: "#fff" }}>Capture</Text>
</Pressable>
</CameraView>
)}
</View>
);
}Lo que esto demuestra:
useCameraPermissions hook para puerta de cámara con fallback de biblioteca.takePictureAsync con tope de calidad antes del paso del manipulador.expo-image-manipulator redimensiona a ancho de 1280px - vista previa segura en memoria.| Módulo | Mejor para | Permiso |
|---|---|---|
expo-camera | Vista previa en vivo, escaneo de código de barras, video | Cámara (+ micrófono para video) |
expo-image-picker | Galería / interfaz del selector del sistema | Biblioteca de fotos |
| Ambas | Flujos de avatar con "Tomar foto" + "Elegir existente" | Solicita solo lo que el usuario selecciona |
Arriesgado: guarda el URI completo de 4032x3024 en React state + renderiza <Image />
Más seguro: manipulateAsync - ancho máximo 1280, JPEG 0.75
Carga: archivo de resolución completa separado en disco si el backend lo requiere - transmite vía FileSystemImage decodifica mapa de bits completo - reduce la escala antes de vista previa.previewUri después de que la carga se complete - libera referencias de archivo.expo-file-system File para cuerpos de carga - ver ./expo-file-system.md.const video = await cameraRef.current?.recordAsync({
maxDuration: 60,
});File.createUploadTask.<CameraView
barcodeScannerSettings={{ barcodeTypes: ["qr"] }}
onBarcodeScanned={({ data }) => handleScan(data)}
/>onBarcodeScanned se dispara rápidamente.| API | Módulo | Propósito |
|---|---|---|
CameraView | expo-camera | Componente de vista previa en vivo |
useCameraPermissions | expo-camera | Hook de permiso |
takePictureAsync | expo-camera | Captura aún |
launchImageLibraryAsync | image-picker | Selección de galería |
launchCameraAsync | image-picker | Intent de cámara del sistema |
manipulateAsync | image-manipulator | Redimensionar/comprimir |
<Image>.launchImageLibraryAsync falla. Solución: requestMediaLibraryPermissionsAsync.| Alternativa | Usarlo cuando | No usarlo cuando |
|---|---|---|
expo-camera | Overlay personalizado, escaneo | Adjuntar simple de una sola vez |
launchCameraAsync | La UX de cámara nativa es suficiente | Marca de visor personalizado |
expo-document-picker | PDFs y documentos | Solo fotos |
| Native vision-camera | Procesadores de marco avanzados | Flujos de captura estándar |
npx expo install expo-camera expo-image-pickerAgrega cadenas de permiso del plugin de configuración, luego reconstruye si los permisos nativos cambiaron.
expo-file-system File.createUploadTask o fetch con cuerpo blob de File.Platform.OS y ofrece entrada de archivo en web.Stack versions: This page was written for React 19.2.3, React Native 0.86.0, and Expo SDK 57 (
expo~57.0.4).
Revisado por Chris St. John·Última actualización: 16 jul 2026