react-native-customizable-image-crop-picker
v1.1.3
Published
Fully customizable React Native image crop picker (JS UI + native performance).
Downloads
1,185
Maintainers
Readme
react-native-customizable-image-crop-picker
A high-performance, fully customizable image crop picker for React Native (iOS + Android).
- Android: system photo picker / camera + uCrop
- iOS:
UIImagePickerController/ camera + TOCropViewController
This library is developed and maintained by Amit Kumar.
Table of contents
- Features
- Roadmap / future features
- Demo
- Installation
- Permissions
- Quick start
- Icons (including SVG on Android)
- API:
openImageCropPicker(options) - Response object
- Errors
- Known limitations
- Peer dependencies
- Contributing
- Support
- License
Features
- Camera + gallery
- Cropping with configurable aspect ratio (
cropWidth/cropHeight) - Circular crop (
circularCrop) - Crop overlays: dimmed layer + grid (
dimmedLayerColor,cropGridEnabled) - Native crop controls/toolbar via
showNativeCropControls - Output compression controls (
compressQuality,compressFormat) - Optional base64 output (
includeBase64) - Native full-screen crop UI customization from JS (header/footer/buttons/icons/layout)
- Icons: remote (http/https) + local (
file:/// Androidcontent://) + base64 + bundled assets (require(...)) - Events:
onCropStart,onCropEnd,onProgress(base64/encoding progress on Android + iOS)
Demo
Installation
npm install react-native-customizable-image-crop-picker --saveor
yarn add react-native-customizable-image-crop-pickeriOS
cd ios
pod install
cd ..Permissions (Info.plist)
Add:
NSCameraUsageDescriptionNSPhotoLibraryUsageDescriptionNSPhotoLibraryAddUsageDescription
If CocoaPods fails: TOCropViewController modular headers
If pod install fails with a Swift modular headers error (TOCropViewController), add this to your app ios/Podfile (inside your app target):
pod 'TOCropViewController', :modular_headers => trueThen run pod install again.
If you see an rsync error about missing simulator slice (RN 0.84+ prebuilt pods)
cd ios
RCT_USE_PREBUILT_RNCORE=0 pod install
cd ..Permissions
iOS (Info.plist)
Add:
NSCameraUsageDescriptionNSPhotoLibraryUsageDescriptionNSPhotoLibraryAddUsageDescription(only if saving to library)
Android
- Request
CAMERApermission at runtime before openingsource: 'camera'. - Ensure the following is present (usually merged from the library):
<uses-permission android:name="android.permission.CAMERA" />If you use remote icons (http/https) for buttons:
<uses-permission android:name="android.permission.INTERNET" />Notes
- iOS Simulator: camera isn’t available on the simulator. Test camera on a real device.
Quick start
import {
openImageCropPicker,
} from 'react-native-customizable-image-crop-picker';Select from gallery
const result = await openImageCropPicker({
source: 'gallery',
cropWidth: 1,
cropHeight: 1,
});
console.log(result.path);Select from camera
const result = await openImageCropPicker({
source: 'camera',
cropWidth: 1,
cropHeight: 1,
});Callbacks / progress (base64)
onProgress emits 0..1 while doing the slow work of generating base64 (only when includeBase64: true).
const result = await openImageCropPicker({
source: 'gallery',
includeBase64: true,
onCropStart: () => console.log('crop started'),
onProgress: (p) => console.log('progress', p), // 0..1 (Android: real streaming encode, iOS: best-effort)
onCropEnd: (res, err) => console.log('crop end', { res, err }),
});Examples (recommended)
Usage
Import:
import {
openImageCropPicker,
} from 'react-native-customizable-image-crop-picker';Select from gallery
const result = await openImageCropPicker({
source: 'gallery',
cropWidth: 1,
cropHeight: 1,
});
console.log(result.path);Select from camera
const result = await openImageCropPicker({
source: 'camera',
cropWidth: 1,
cropHeight: 1,
});Icons (including SVG on Android)
Button icon props like uploadButtonIconUri / cancelButtonIconUri accept:
- Remote URI:
https://... - Local file:
file://... - Android content URI:
content://... - Bundled asset:
require('./icon.png')(recommended)
SVG
- Android native UI supports SVG for these icon URIs.
- iOS native UI does not support SVG (use PNG/JPG/WebP).
API: openImageCropPicker(options)
Backwards compatible alias:
open(options)
Defaults shown below are the current native defaults.
Picker options
| Prop | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| source | 'camera' \| 'gallery' | Yes | - | Open camera or gallery |
| mediaType | 'photo' | No | 'photo' | Media type (photos only) |
Crop options
| Prop | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| cropWidth | number | No | 100 | Aspect ratio width (native crop) |
| cropHeight | number | No | 100 | Aspect ratio height (native crop) |
| aspectRatio | { width: number; height: number } | No | - | Alternative to cropWidth/cropHeight |
| freeStyleCropEnabled | boolean | No | false | If true, user can resize the crop box (free-style) |
| circularCrop | boolean | No | false | Circular crop (iOS/Android) |
| cropGridEnabled | boolean | No | false | Show crop grid (iOS/Android) |
| cropOverlayColor | string | No | - | Alias for dimmedLayerColor |
| cropFrameColor | string | No | - | Android: crop frame color |
| cropGridColor | string | No | - | Android: crop grid color. iOS: used for circular grid overlay |
| compressQuality | number | No | 1 | 0..1 (output encoding quality) |
| compressFormat | 'jpeg' \| 'png' \| 'webp' | No | 'jpeg' | Output encoding format (iOS WebP is best-effort; falls back to JPEG if unavailable) |
| includeBase64 | boolean | No | false | If true, resolves base64 (slower) |
Native full-screen crop UI (header/footer/buttons)
| Prop | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| preferNativeUI | boolean | No | true | Prefer native full-screen UI (current implementation uses native UI) |
| headerTitle | string | No | Preview Image | Native header title |
| headerAlignment | 'left' \| 'center' \| 'right' | No | 'center' | Header title alignment |
| headerHeight | number | No | 84 | Header height |
| headerPaddingHorizontal | number | No | 20 | Header padding |
| headerPaddingTop | number | No | 20 | Header padding |
| headerPaddingBottom | number | No | 20 | Header padding |
| controlsPlacement | 'bottom' \| 'top' | No | 'bottom' | Render actions in footer or header |
| topLeftControl | 'cancel' \| 'upload' \| 'none' | No | 'cancel' | Left header control when controlsPlacement: 'top' |
| topRightControl | 'cancel' \| 'upload' \| 'none' | No | 'upload' | Right header control when controlsPlacement: 'top' |
| cancelText | string | No | Cancel | Cancel label |
| uploadText | string | No | Upload | Upload label |
| footerPaddingHorizontal | number | No | 20 | Footer padding |
| footerPaddingTop | number | No | 16 | Footer padding |
| footerPaddingBottom | number | No | 24 | Footer padding |
| footerButtonGap | number | No | 12 | Gap between footer buttons |
| footerButtonHeight | number | No | 54 | Button height |
| footerButtonLayout | 'vertical' \| 'horizontal' | No | 'vertical' | Footer layout |
| footerButtonOrder | 'uploadFirst' \| 'cancelFirst' | No | 'uploadFirst' | Footer order |
| cancelButtonRadius | number | No | 28 | Cancel button radius |
| uploadButtonRadius | number | No | 28 | Upload button radius |
Button content + icons
| Prop | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| uploadButtonContent | 'text' \| 'icon' \| 'iconText' \| 'textIcon' \| 'TextIcon' \| 'icon+text' \| 'text+icon' | No | 'text' | Upload content mode |
| cancelButtonContent | same as above | No | 'text' | Cancel content mode |
| uploadButtonIconUri | string \| ImageSourcePropType | No | '' | Icon URI (http/https/file/content) or bundled asset require(...) |
| cancelButtonIconUri | string \| ImageSourcePropType | No | '' | Icon URI (http/https/file/content) or bundled asset require(...) |
| uploadButtonIconBase64 | string | No | '' | Icon base64 (optionally data:image/...;base64,) |
| cancelButtonIconBase64 | string | No | '' | Icon base64 |
| uploadButtonIconSize | number | No | 18 | Icon size |
| cancelButtonIconSize | number | No | 18 | Icon size |
| buttonIconGap | number | No | 8 | Gap between icon and text |
| uploadButtonIconTintColor | string | No | '' | Tint (only applied if provided) |
| cancelButtonIconTintColor | string | No | '' | Tint (only applied if provided) |
| buttonContentPaddingHorizontal | number | No | 12 | Internal button padding (both buttons) |
| buttonContentPaddingVertical | number | No | 0 | Internal button padding (both buttons) |
| uploadButtonContentPaddingHorizontal | number | No | - | Upload internal padding override |
| uploadButtonContentPaddingVertical | number | No | - | Upload internal padding override |
| cancelButtonContentPaddingHorizontal | number | No | - | Cancel internal padding override |
| cancelButtonContentPaddingVertical | number | No | - | Cancel internal padding override |
Using bundled images (require(...))
uploadButtonIconUri / cancelButtonIconUri accept:
- a string URI (
https://,file://, etc.) - or a bundled asset via
require(...)(recommended)
import { openImageCropPicker } from 'react-native-customizable-image-crop-picker';
await openImageCropPicker({
source: 'gallery',
uploadButtonContent: 'icon',
uploadButtonIconUri: require('./upload.jpg'),
});Theme / status bar / overlay
| Prop | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| isDarkTheme | boolean | No | false | Native theme hint |
| statusBarColor | string | No | #FFFFFF (light), #000000 (dark) | Status bar color |
| statusBarStyle | 'dark' \| 'light' | No | - | Android only: status bar icon/text color |
| dimmedLayerColor | string | No | #B3000000 (light), #E0000000 (dark) | Dimmed overlay around crop box |
| showNativeCropControls | boolean | No | false | Show native crop controls/toolbar |
showNativeCropControls + controlsPlacement
When showNativeCropControls: true, the native crop toolbar is visible.
- To avoid the footer covering the native toolbar,
showNativeCropControlsworks only withcontrolsPlacement: 'top'. - If you pass
controlsPlacement: 'bottom', the library will auto-force it to'top'.
Platform-specific props / behavior
Android only
drawUnderStatusBar: edge-to-edge header under the status bar.statusBarStyle: controls status bar icons/text ('dark' | 'light').cropFrameColor: crop frame color.cropGridColor: crop grid line color (rectangle mode).- SVG button icons:
uploadButtonIconUri/cancelButtonIconUrisupport SVG on Android native UI.
iOS only
compressFormat: 'webp': best-effort (falls back to JPEG if the encoder/type is unavailable).
Both (with small differences)
cropGridEnabled: rectangle uses native grid on iOS; circular uses a custom grid overlay.showNativeCropControls: requires header controls (controlsPlacement: 'top'), library auto-forces when enabled.
Style objects (partial support)
These are read from React Native styles and mapped into native values (subset only).
| Prop | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| headerStyle | { containerStyle?, titleStyle? } | No | - | Supports containerStyle.backgroundColor, titleStyle.color/fontSize/fontFamily |
| footerStyle | { containerStyle?, cancelButtonStyle?, uploadButtonStyle?, cancelTextStyle?, uploadTextStyle? } | No | - | Supports background/border/textColor/fontSize/fontFamily/borderRadius subset |
Response Object
| Property | Type | Description |
| --- | --- | --- |
| path | string | Cropped image URI/path |
| width | number | Image width (if available) |
| height | number | Image height (if available) |
| mime | string | MIME type (if available) |
| size | number | Size in bytes (if available) |
| exif | object | EXIF metadata (if available) |
| base64 | string | Only when includeBase64: true |
Errors
The library throws CropPickerError with a code:
E_PICKER_CANCELLEDE_PERMISSION_MISSINGE_NO_IMAGE_DATA_FOUNDE_NO_APP_AVAILABLEE_PICKER_ERRORE_ACTIVITY_DOES_NOT_EXISTE_MODULE_DESTROYEDE_UNAVAILABLEE_INVALID_OPTIONS
Known limitations
- Multiple selection options exist in types but native multiple picking is not implemented yet.
- iOS grid/frame color customization is limited (rectangle grid uses the native overlay; circular grid uses a custom overlay).
Peer Dependencies
| Package | Version |
| --- | --- |
| react | >=18 |
| react-native | >=0.72 |
Contributing
Contributions are welcome. Please open an issue with reproduction details or submit a PR with a clear description + test plan.
Support
- Bug reports / feature requests: open an issue with a minimal repro (device/OS, RN version, and logs).
- Questions / help: start a GitHub discussion or issue (whichever you prefer) with your use-case and desired UI.
License
MIT
