@pigayazilim/react-native-camera
v1.0.5
Published
Reusable React Native camera module with photo/video capture, trimming, GIF export and image cropping
Maintainers
Readme
Camera Module
A reusable React Native camera module with photo/video capture, gallery access, video trimming, GIF export, and image cropping. Works on both Android and iOS with zero extra packages — all video processing is done via native platform APIs (ExoPlayer/Media3 on Android, AVFoundation on iOS).
Features
- Photo & video capture with flash support
- Pinch-to-zoom and double-tap to flip camera
- Multi-select mode for batch photo/video selection
- Gallery access (CameraRoll)
- Video trimming and GIF export (Android: Media3, iOS: AVFoundation + ImageIO)
- Image crop overlay
- Fully themeable via
ThemeProvider - Customizable UI labels (i18n friendly)
Installation
1. Install the package
npm install @pigayazilim/react-native-camera
# or
yarn add @pigayazilim/react-native-camera2. Install peer dependencies
npm install \
@react-native-camera-roll/camera-roll \
react-native-gesture-handler \
react-native-reanimated \
react-native-worklets \
react-native-safe-area-context \
react-native-svg \
react-native-svg-transformer \
react-native-vision-camera3. Verify autolinking
npx react-native autolinking-statusYou should see camera-module listed and linked for both Android and iOS.
Native Setup
Android
android/app/src/main/AndroidManifest.xml
Add the following permissions inside <manifest>:
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<!-- API 33+ -->
<uses-permission android:name="android.permission.READ_MEDIA_IMAGES" />
<uses-permission android:name="android.permission.READ_MEDIA_VIDEO" />
<!-- API < 33 -->
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"
android:maxSdkVersion="32" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"
android:maxSdkVersion="29" />android/app/build.gradle
Ensure minSdkVersion is at least 24:
android {
defaultConfig {
minSdkVersion 24
}
}Note: If autolinking doesn't add the module automatically, add this to
android/settings.gradle:include ':camera-module' project(':camera-module').projectDir = new File(rootProject.projectDir, '../modules/camera/android')
iOS
ios/YourApp/Info.plist
Add the following permission descriptions:
<key>NSCameraUsageDescription</key>
<string>This app needs camera access to take photos and videos.</string>
<key>NSMicrophoneUsageDescription</key>
<string>This app needs microphone access to record videos.</string>
<key>NSPhotoLibraryUsageDescription</key>
<string>This app needs photo library access to select media.</string>
<key>NSPhotoLibraryAddUsageDescription</key>
<string>This app needs permission to save photos to your library.</string>Install Pods
cd ios && bundle exec pod install && cd ..Build Config Setup
babel.config.js
react-native-reanimated plugin must be the last plugin:
module.exports = {
presets: ['module:@react-native/babel-preset'],
plugins: [
// ... your other plugins
'react-native-reanimated/plugin',
],
};metro.config.js
Configure react-native-svg-transformer for SVG support:
const { getDefaultConfig, mergeConfig } = require('@react-native/metro-config');
const { withSVGTransformer } = require('react-native-svg-transformer/react-native');
module.exports = mergeConfig(
withSVGTransformer(getDefaultConfig(__dirname)),
{
// your other metro config
}
);declarations.d.ts (TypeScript only)
Add SVG module declaration to your project:
declare module '*.svg' {
import React from 'react';
import { SvgProps } from 'react-native-svg';
const content: React.FC<SvgProps>;
export default content;
}Wrapping Your App
Your app's root must be wrapped with GestureHandlerRootView and SafeAreaProvider:
import { GestureHandlerRootView } from 'react-native-gesture-handler';
import { SafeAreaProvider } from 'react-native-safe-area-context';
export default function Root() {
return (
<GestureHandlerRootView style={{ flex: 1 }}>
<SafeAreaProvider>
<App />
</SafeAreaProvider>
</GestureHandlerRootView>
);
}Usage
import { CameraModal, usePermissions } from '@pigayazilim/react-native-camera';
export default function MyScreen() {
const [cameraVisible, setCameraVisible] = useState(false);
const { hasAllPermissions, checkAndRequestPermissions } = usePermissions();
const handleOpen = async () => {
await checkAndRequestPermissions();
setCameraVisible(true);
};
const handleConfirm = (media: Array<{ uri: string; type: string; name: string }>) => {
console.log('Selected media:', media);
setCameraVisible(false);
};
return (
<>
<Button title="Open Camera" onPress={handleOpen} />
<CameraModal
visible={cameraVisible}
onClose={() => setCameraVisible(false)}
onConfirm={handleConfirm}
permissionGranted={hasAllPermissions}
maxVideoDuration={60}
/>
</>
);
}API
<CameraModal />
| Prop | Type | Required | Default | Description |
|------|------|----------|---------|-------------|
| visible | boolean | Yes | — | Controls modal visibility |
| onClose | () => void | Yes | — | Called when modal is dismissed |
| onConfirm | (media: MediaItem[]) => void | Yes | — | Called with selected media array |
| permissionGranted | boolean | Yes | — | Whether camera/mic permissions are granted |
| maxVideoDuration | number | No | 60 | Max video recording duration in seconds |
| theme | Partial<ThemeColors> | No | — | Override default theme colors |
| labels | Partial<CameraLabels> | No | — | Override default Turkish UI labels |
MediaItem (returned in onConfirm)
{
uri: string; // File URI
type: string; // "image/jpeg" | "image/png" | "video/mp4"
name: string; // e.g. "chatPhoto_1234567890.jpeg"
}usePermissions()
const {
hasAllPermissions, // boolean — all required permissions granted
isChecking, // boolean — permission check in progress
hasCameraPermission, // boolean — camera only
hasMicPermission, // boolean — microphone only
checkAndRequestPermissions, // () => Promise<boolean> — trigger request
} = usePermissions();<ThemeProvider />
Wrap your screen (or the whole app) to apply a custom theme:
import { ThemeProvider } from '@pigayazilim/react-native-camera';
<ThemeProvider colors={{ primary: '#FF6B00', gold: '#FFC300' }}>
<CameraModal ... />
</ThemeProvider>ThemeColors
{
primary: string;
white: string;
black: string;
error: string;
gold: string;
overlay: string;
surface: string;
border: string;
shadow: string;
textSecondary: string;
redSoft: string;
redMedium: string;
}CameraLabels
Override any UI text (all optional, Turkish defaults):
type CameraLabels = {
photoMode?: string; // default: "FOTOĞRAF"
videoMode?: string; // default: "VİDEO"
permissionDenied?: string; // default: "Kamera izni verilmedi."
cameraNotFound?: string; // default: "Kamera bulunamadı."
cameraStarting?: string; // default: "Kamera başlatılıyor..."
errorTitle?: string; // default: "Hata"
micPermissionError?: string; // default: "Video çekmek için mikrofon izni gerekli."
recordingError?: string; // default: "Video kaydı sırasında bir sorun oluştu"
selectionConflictError?: string; // default: "Öncelikle seçili medyayı onaylamalı veya iptal etmelisiniz."
};Example — English labels:
<CameraModal
...
labels={{
photoMode: 'PHOTO',
videoMode: 'VIDEO',
permissionDenied: 'Camera permission denied.',
cameraNotFound: 'No camera found.',
cameraStarting: 'Starting camera...',
errorTitle: 'Error',
micPermissionError: 'Microphone permission is required to record video.',
recordingError: 'An error occurred during video recording.',
selectionConflictError: 'Please confirm or cancel your current selection first.',
}}
/>Native Video Processing
| Feature | Android | iOS | |---------|---------|-----| | Video playback | ExoPlayer (Media3) | AVPlayer | | Video trim | Media3 Transformer | AVAssetExportSession | | GIF export | Custom GifEncoder | CGImageDestination (ImageIO) | | Thumbnails | MediaMetadataRetriever | AVAssetImageGenerator | | Image crop | BitmapRegionDecoder | CGImage.cropping |
All native code is bundled in the android/ and ios/ folders. No external npm packages are required for video processing.
