capacitor-camera-crop
v2.0.0
Published
A Capacitor plugin providing native camera and crop support.
Maintainers
Readme
capacitor-camera-crop
A Capacitor plugin providing native camera and crop support for iOS and Android.
Features
- ✅ Open native camera or gallery
- ✅ Native image cropping with customizable aspect ratios
- ✅ Returns file URI or base64 encoded string
- ✅ Image resizing support
- ✅ TypeScript support
- ✅ iOS (Swift) and Android (Kotlin) implementations
Installation
npm install capacitor-camera-crop
# or
bun install capacitor-camera-cropThen sync your Capacitor project:
npx cap syncRequirements
- Capacitor 7 or 8
- iOS 14.0+
- Android API 23+ (Android 6.0+)
Platform support
| Platform | Supported | Notes |
|----------|-----------|-------|
| iOS | ✅ | UIImagePicker / PHPicker + TOCropViewController |
| Android | ✅ | ACTION_IMAGE_CAPTURE / ACTION_PICK + uCrop |
| Web | ❌ | captureAndCrop() rejects with an unimplemented error |
Cropping is available on both native platforms. useSystemEditingIfAvailable
affects iOS only (see the options table); on Android, free-vs-locked cropping is
controlled solely by nativeCropping.
iOS Setup
Add the following keys to your Info.plist:
<key>NSCameraUsageDescription</key>
<string>We need camera access to take pictures.</string>
<key>NSPhotoLibraryUsageDescription</key>
<string>We need access to your photo library.</string>
<key>NSPhotoLibraryAddUsageDescription</key>
<string>We need to save cropped photos to your library.</string>Android Setup
This plugin uses uCrop for cropping, which is published on JitPack. Add the JitPack repository to your app's root android/build.gradle (or settings.gradle if you use centralized repositories):
allprojects {
repositories {
google()
mavenCentral()
maven { url 'https://jitpack.io' }
}
}Permissions
This plugin does not require the CAMERA or READ_MEDIA_IMAGES permissions. It uses delegated intents — ACTION_IMAGE_CAPTURE (system camera app) and ACTION_PICK (system gallery) — which run in those apps and hand back a URI your app is temporarily granted to read.
⚠️ Do not add
<uses-permission android:name="android.permission.CAMERA" />to your manifest for this plugin. DeclaringCAMERAwithout requesting it at runtime causes Android to blockACTION_IMAGE_CAPTUREwith a permission-denial crash. Only addCAMERAif some other part of your app uses the camera directly, and then you must request it at runtime yourself.
FileProvider
You need a FileProvider in your app's AndroidManifest.xml (used to hand the camera app a URI to write the captured photo into):
<provider
android:name="androidx.core.content.FileProvider"
android:authorities="${applicationId}.fileprovider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/file_paths" />
</provider>Create android/app/src/main/res/xml/file_paths.xml:
<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
<cache-path name="cache" path="." />
<external-cache-path name="external_cache" path="." />
</paths>Usage
import { CapacitorCameraCrop } from 'capacitor-camera-crop';
// Open camera with cropping
async function takePicture() {
try {
const result = await CapacitorCameraCrop.captureAndCrop({
source: 'camera',
enableCropping: true,
aspectRatio: '1:1',
resultType: 'uri',
quality: 90,
});
console.log('Image URI:', result.value);
console.log('Dimensions:', result.width, 'x', result.height);
} catch (error) {
console.error('Error:', error);
}
}
// Open gallery without cropping
async function selectImage() {
try {
const result = await CapacitorCameraCrop.captureAndCrop({
source: 'gallery',
enableCropping: false,
resultType: 'uri',
});
console.log('Image URI:', result.value);
} catch (error) {
console.error('Error:', error);
}
}
// Get base64 encoded image with custom aspect ratio
async function captureBase64() {
try {
const result = await CapacitorCameraCrop.captureAndCrop({
source: 'camera',
enableCropping: true,
aspectRatio: { x: 16, y: 9 },
resultType: 'base64',
width: 1920,
height: 1080,
quality: 85,
});
console.log('Base64 image:', result.value);
} catch (error) {
console.error('Error:', error);
}
}
// Use native crop controller (TOCropViewController on iOS, UCrop on Android)
async function captureWithNativeCropper() {
try {
const result = await CapacitorCameraCrop.captureAndCrop({
source: 'camera',
enableCropping: true,
nativeCropping: true, // Uses TOCropViewController on iOS, UCrop on Android
aspectRatio: '1:1',
resultType: 'uri',
quality: 90,
});
console.log('Cropped image:', result.value);
} catch (error) {
console.error('Error:', error);
}
}API
captureAndCrop(options?: CaptureAndCropOptions): Promise<CaptureAndCropResult>
Opens the camera or gallery, optionally crops the image, and returns the result.
CaptureAndCropOptions
| Property | Type | Default | Description |
|----------|------|---------|-------------|
| source | 'camera' \| 'gallery' | 'camera' | Source to pick the image from |
| enableCropping | boolean | false | Enable cropping after capturing/selecting |
| aspectRatio | 'free' \| '1:1' \| '4:3' \| '16:9' \| { x: number; y: number } | 'free' | Aspect ratio for cropping |
| resultType | 'uri' \| 'base64' | 'uri' | Result type: file URI or base64 encoded string |
| width | number | - | Maximum width (px) for the output image. Honored on both platforms; may be set independently of height |
| height | number | - | Maximum height (px) for the output image. Honored on both platforms; may be set independently of width |
| quality | number | 90 | JPEG quality (clamped to 0-100) |
| useSystemEditingIfAvailable | boolean | true | iOS only. Use the built-in UIImagePicker editor when cropping. Ignored when nativeCropping=true. Has no effect on Android (free-vs-locked is controlled by nativeCropping) |
| nativeCropping | boolean | false | Use the native crop controller (TOCropViewController on iOS, locked-aspect uCrop on Android). Overrides useSystemEditingIfAvailable on iOS |
CaptureAndCropResult
| Property | Type | Description |
|----------|------|-------------|
| value | string | The file URI or base64 encoded string |
| mimeType | string | MIME type of the returned image |
| width | number | Width of the image in pixels |
| height | number | Height of the image in pixels |
Development
Building
bun install
bun run buildExample app
A runnable test harness lives in example/. It installs the
plugin from the repo root and exercises captureAndCrop across every option
on iOS and Android. See example/README.md for setup
and the acceptance-test matrix.
License
MIT
Contributing
Contributions are welcome! See CONTRIBUTING.md for local
setup, how to test native changes with the example/ app, and conventions.
