--- name: "add-expo-camera" description: "Add expo-camera for in-app camera, video recording, barcode scanning. Use when the user says 'camera', 'expo-camera', 'take photo', 'barcode scan', 'QR scanner'." --- # Add Expo Camera In-app camera for photo, video, barcode/QR. ## Install ```bash npm exec --no -- expo install expo-camera ``` ## app.json ```json { "expo": { "plugins": [ ["expo-camera", { "cameraPermission": "Allow $(PRODUCT_NAME) to access your camera." }] ] } } ``` ## Photo capture ```tsx import { CameraView, useCameraPermissions } from 'expo-camera'; import { useRef, useState } from 'react'; export default function CameraScreen() { const [permission, requestPermission] = useCameraPermissions(); const [photo, setPhoto] = useState(null); const ref = useRef(null); if (!permission) return ; if (!permission.granted) return Grant camera; const takePhoto = async () => { const result = await ref.current?.takePictureAsync({ quality: 0.8 }); if (result?.uri) setPhoto(result.uri); }; return ( ); } ``` ## Barcode / QR scanning ```tsx const handleBarcodeScanned = ({ data }: { data: string }) => { if (!/^[A-Za-z0-9._~-]{1,256}$/.test(data)) { showInvalidCodeMessage(); return; } router.push({ pathname: '/scanned/[data]', params: { data } }); }; ``` ## Permission pre-prompt Always pre-prompt with custom UI explaining WHY before triggering system dialog. iOS only gives ONE shot — denied = locked out unless user opens Settings manually. Treat barcode data as untrusted input. Validate its expected format and pass it as a structured route parameter; do not concatenate it into a navigation URL or use it to open arbitrary links. ## Reference `references/01-expo-sdk-54.md`