@pigayazilim/react-native-camera
v1.1.0
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
- Optional gallery strip (CameraRoll) and full gallery picker (Storage Access Framework — no permissions needed)
- Video trimming and GIF export (Android: Media3, iOS: AVFoundation + ImageIO)
- Image crop overlay
- Fully themeable via
ThemeProvider - Customizable UI labels (i18n friendly)
- Photo-only mode (disables video recording and microphone permission)
- Configurable gallery picker options (single/multi select, photo/video filter)
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
Minimum required permissions:
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.RECORD_AUDIO" />Only add media permissions if you enable the gallery strip (enableGalleryStrip={true}):
<!-- 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" />Play Store note: Do not declare
READ_MEDIA_IMAGES/READ_MEDIA_VIDEOunless you useenableGalleryStrip. The full gallery picker uses Android's Storage Access Framework and requires no permissions.
android/app/build.gradle
Ensure minSdkVersion is at least 24:
android {
defaultConfig {
minSdkVersion 24
}
}iOS
ios/YourApp/Info.plist
Minimum required keys:
<key>NSCameraUsageDescription</key>
<string>This app needs camera access to take photos and videos.</string>
<key>NSPhotoLibraryUsageDescription</key>
<string>This app needs photo library access to select media.</string>Add microphone key only if video is enabled:
<key>NSMicrophoneUsageDescription</key>
<string>This app needs microphone access to record videos.</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)
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
Default (photo + video)
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);
};
return (
<>
<Button title="Open Camera" onPress={handleOpen} />
<CameraModal
visible={cameraVisible}
onClose={() => setCameraVisible(false)}
onConfirm={(media) => console.log(media)}
permissionGranted={hasAllPermissions}
maxVideoDuration={60}
/>
</>
);
}Photo-only mode (Play Store safe — no mic permission)
const { hasAllPermissions } = usePermissions({ enableVideo: false });
<CameraModal
visible={cameraVisible}
onClose={() => setCameraVisible(false)}
onConfirm={handleConfirm}
permissionGranted={hasAllPermissions}
enableVideo={false}
/>With gallery strip (requires media permissions)
const { hasAllPermissions } = usePermissions({ includeMedia: true });
<CameraModal
visible={cameraVisible}
onClose={() => setCameraVisible(false)}
onConfirm={handleConfirm}
permissionGranted={hasAllPermissions}
enableGalleryStrip={true}
/>Single photo picker from gallery (no permissions)
<CameraModal
visible={cameraVisible}
onClose={() => setCameraVisible(false)}
onConfirm={handleConfirm}
permissionGranted={hasAllPermissions}
enableVideo={false}
galleryOptions={{ allowMultiple: false, allowVideo: false }}
/>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 required 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 |
| enableVideo | boolean | No | true | Enable video recording and mode switcher |
| enableGalleryStrip | boolean | No | false | Show horizontal gallery strip (requires media permissions) |
| galleryOptions | GalleryOptions | No | { allowMultiple: true, allowVideo: true } | Configure the full gallery picker |
GalleryOptions
{
allowMultiple?: boolean; // default: true — allow selecting multiple files
allowVideo?: boolean; // default: true — include videos in picker
}MediaItem (returned in onConfirm)
{
uri: string; // File URI
type: string; // "image/jpeg" | "image/png" | "video/mp4"
name: string; // e.g. "chatPhoto_1234567890.jpeg"
}usePermissions(options?)
const {
hasAllPermissions, // boolean — all requested permissions granted
isChecking, // boolean — permission check in progress
hasCameraPermission, // boolean — camera only
hasMicPermission, // boolean — microphone only
checkAndRequestPermissions, // () => Promise<boolean> — trigger request
} = usePermissions({
enableVideo?: boolean, // default: true — if false, mic permission is skipped
includeMedia?: boolean, // default: false — if true, READ_MEDIA_* is requested (Android)
});| Option | Type | Default | Description |
|--------|------|---------|-------------|
| enableVideo | boolean | true | Request microphone permission for video recording |
| includeMedia | boolean | false | Request READ_MEDIA_IMAGES / READ_MEDIA_VIDEO for gallery strip |
<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.
