React Native | Image Crop Picker

React Native Image Crop Picker

If you are looking for a production-ready way to pick an image or video, crop it, and access the EXIF metadata natively on iOS and Android from React Native, `react-native-image-crop-picker` is a good option to consider. This documentation will go over installation, platform configuration, and usage patterns with TypeScript snippets.

React native

What does the Image Crop Picker Library do?

  • Pick from gallery: Single or multiple selections, image and video.
  • Crop: Built-in native crop UI with aspect ratio, free-form cropping, and circular overlays.
  • Camera: Capture a new photo or video.
  • Metadata: Provides useful properties like `path`, `width`, `height`, `mime`, `size`, `cropRect`, and more.
  • File handling: Temporary files with cleanup helpers.

Prerequisites

  • React Native 0.60+ (autolinking supported)
  • Xcode for iOS builds; Android Studio/SDKs for Android
  • Not compatible with Expo Managed workflow without EAS + config plugins (consider Expo’s own `ImagePicker` there)

Installation

npm install react-native-image-crop-picker

Or

yarn add react-native-image-crop-picker

iOS CocoaPods (from the `ios` directory):

cd ios && pod install && cd ..

iOS Setup

Add the following usage descriptions to `ios/YourApp/Info.plist`:

<key>NSPhotoLibraryUsageDescription</key>
<string>We need access to your photo library to let you select images.</string>
<key>NSCameraUsageDescription</key>
<string>We need access to your camera to let you take photos.</string>
<key>NSPhotoLibraryAddUsageDescription</key>
<string>We need permission to save edited photos to your library.</string>

Notes:

  • RN 0.60+ uses autolinking—no manual linking needed.
  • If you see Pod cache issues, try `pod repo update` or remove `Podfile.lock` and `Pods/` before re-installing.

Android Setup

Permissions in `android/app/src/main/AndroidManifest.xml`:

<manifest ...>
  <uses-permission android:name="android.permission.CAMERA" />
  <!-- Android 13+ -->
  <uses-permission android:name="android.permission.READ_MEDIA_IMAGES" />
  <!-- For Android 12 and below -->
  <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" android:maxSdkVersion="32" />
  <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" android:maxSdkVersion="29" />
  <!-- Optional if you capture video -->
  <uses-permission android:name="android.permission.RECORD_AUDIO" />
  <application ...>
    <!-- If you have a custom FileProvider, ensure authorities don’t conflict -->
  </application>
</manifest>

Gradle recommendations (in `android/build.gradle` or `android/gradle.properties`):

  • Target modern SDK (e.g., `targetSdkVersion 34+`)
  • Ensure `minSdkVersion` meets the library’s requirement (commonly 21+)
  • Keep AndroidX enabled

Runtime permissions:

  • On Android 6.0+ you must request permissions at runtime (shown later).

Example

import ImagePicker, {  Image as PickerImage,  Video as ickerVideo,} from 'react-native-image-crop-picker';

// Open gallery
ImagePicker.openPicker({ cropping: true })

// Open camera
ImagePicker.openCamera({ cropping: true })

// Crop existing image
ImagePicker.openCropper({ path: 'file:///path/to/image.jpg', width: 800, height: 600 })

// Multiple select
ImagePicker.openPicker({ multiple: true })

// Cleanup temp images
ImagePicker.clean()
ImagePicker.cleanSingle(path)

Example: Pick, Crop, and Display an Image (TypeScript)

import React, { useState, useCallback } from 'react';
import { Alert, Image, Platform, PermissionsAndroid, StyleSheet, Text, TouchableOpacity, View } from 'react-native';
import ImagePicker, { Image as PickerImage } from 'react-native-image-crop-picker';

async function ensureAndroidPermissions(): Promise<boolean> {
  if (Platform.OS !== 'android') return true;
  const permissionsToRequest: string[] = [];

  // For Android 13+ use READ_MEDIA_IMAGES, else READ_EXTERNAL_STORAGE

  if (Platform.Version >= 33) {
    permissionsToRequest.push('android.permission.READ_MEDIA_IMAGES');
  } else {
    permissionsToRequest.push('android.permission.READ_EXTERNAL_STORAGE');
  }

  // If you plan to use the camera:

  permissionsToRequest.push('android.permission.CAMERA');
  const results = await PermissionsAndroid.requestMultiple(permissionsToRequest);
  const allGranted = Object.values(results).every(v => v === PermissionsAndroid.RESULTS.GRANTED);

  if (!allGranted) {
    Alert.alert('Permission required', 'Please grant media and camera permissions.');
  }

  return allGranted;

}

export default function ImageCropExample() {
  const [image, setImage] = useState<PickerImage | null>(null);
  const pickAndCrop = useCallback(async () => {

    try {
      if (!(await ensureAndroidPermissions())) return;
      const picked = await ImagePicker.openPicker({
        width: 800,
        height: 600,
        cropping: true,
        cropperCircleOverlay: false,
        compressImageQuality: 0.85, // 0..1
        mediaType: 'photo',
        includeExif: true,
        forceJpg: true,
      });

      setImage(picked);
    } catch (e: any) {

      if (e?.message?.includes('cancelled')) return;
      Alert.alert('Picker error', e?.message ?? String(e));
    }
  }, []);

  const captureAndCrop = useCallback(async () => {
    try {
      if (!(await ensureAndroidPermissions())) return;
      const captured = await ImagePicker.openCamera({
        width: 1000,
        height: 1000,
        cropping: true,
        freeStyleCropEnabled: true,
        includeExif: true,
      });

      setImage(captured);
    } catch (e: any) {
      if (e?.message?.includes('cancelled')) return;
      Alert.alert('Camera error', e?.message ?? String(e));
    }
  }, []);

  const clearTempFiles = useCallback(async () => {
    try {
      if (image?.path) {
        await ImagePicker.cleanSingle(image.path);
      }
      await ImagePicker.clean();
      setImage(null);
    } catch (e: any) {
      Alert.alert('Cleanup error', e?.message ?? String(e));
    }
  }, [image]);

  return (
    <View style={styles.container}>
      <Text style={styles.title}>Image Crop Picker Demo</Text>
      <View style={styles.actions}>
        <TouchableOpacity style={styles.button} onPress={pickAndCrop}>
          <Text style={styles.buttonText}>Pick from Gallery</Text>
        </TouchableOpacity>
        <TouchableOpacity style={styles.button} onPress={captureAndCrop}>
          <Text style={styles.buttonText}>Open Camera</Text>
        </TouchableOpacity>
        <TouchableOpacity style={[styles.button, styles.danger]} onPress={clearTempFiles}>
          <Text style={styles.buttonText}>Clear</Text>
        </TouchableOpacity>
      </View>
      {image?.path ? (
        <View style={styles.preview}>
          <Image
            source={{ uri: image.path }}
            style={styles.image}
            resizeMode="cover"
          />

          <Text style={styles.meta}>
            {image.width}×{image.height} • {image.mime} • {((image.size ?? 0) / 1024 / 1024).toFixed(2)}MB
          </Text>
        </View>
      ) : (
        <Text style={styles.help}>Pick or capture an image to preview it here.</Text>
      )}
    </View>
  );
}
const styles = StyleSheet.create({
  container: { flex: 1, padding: 16, backgroundColor: '#0b0b0b' },
  title: { color: 'white', fontSize: 18, fontWeight: '600', marginBottom: 12 },
  actions: { flexDirection: 'row', gap: 8, flexWrap: 'wrap' },
  button: { backgroundColor: '#2b6ef2', paddingHorizontal: 14, paddingVertical: 10, borderRadius: 8 },
  danger: { backgroundColor: '#cc3344' },
  buttonText: { color: 'white', fontWeight: '600' },
  preview: { marginTop: 16, alignItems: 'center' },
  image: { width: '100%', aspectRatio: 4 / 3, borderRadius: 12 },
  meta: { color: '#c7c7c7', marginTop: 8 },
  help: { color: '#9aa0a6', marginTop: 16 },
});

Multiple Selection Example

const pickMultiple = async () => {
  try {
    const items = await ImagePicker.openPicker({
      multiple: true,
      waitAnimationEnd: false,
      cropping: false, // you can also enable batch cropping if needed
      mediaType: 'photo',
    });

    // items is an array of images

    console.log('Selected', items.length, 'images');
  } catch (e) {
    // handle cancel/error
  }
};

Cropping an Existing File by Path

const cropExisting = async (path: string) => {
  const result = await ImagePicker.openCropper({
    path,
    width: 1200,
    height: 800,
    freeStyleCropEnabled: true,
    showCropGuidelines: true,
  });
  return result.path;
};

Handling Runtime Permissions (Android)

  • For Android 13+ request `READ_MEDIA_IMAGES`; for 12 and below request `READ_EXTERNAL_STORAGE`.
  • If using a camera, also request `CAMERA`.
  • You can use `PermissionsAndroid` (shown above) or a dedicated library like `react-native-permissions` if you prefer unified flows.

Conclusion:

The React Native Image Crop Picker library is a must-have tool for developers working on apps that involve image or video uploads. Its cropping, multiple selection, and camera integration features make it a better alternative to basic pickers.

If you want to enhance your app’s user experience with smooth media selection, this library is the way to go.

For any queries, let me know through the comment section.

Shopify Mobile App Builder

FAQ

  1. What is React Native Image Crop Picker used for?

React Native Image Crop Picker is a widely used library that enables developers to easily pick images and videos from the gallery or camera. The library also supports cropping, picking images in bulk, and also supports base64 encoding.

  1. How do I install React Native Image Crop Picker?

It can be installed via:

npm install react-native-image-crop-picker

Or

yarn add react-native-image-crop-picker

If you’re developing for iOS, you also need to run cd ios && pod install.

  1. Can I crop images with this library?

Yes. The library has built-in cropping functionality. By setting the option cropping: true, users can crop images to specific dimensions.

  1. Can I use the camera with React Native Image Crop Picker?

Yes. The library enables you to open the device camera and take images directly using:

ImagePicker.openCamera({…});

Previous Article

SEO Case Study: The Baking Tools - Online Baking Supply Store, India

Write a Comment

Leave a Comment

Your email address will not be published. Required fields are marked *

Get Connect With Us

Subscribe to our email newsletter to get the latest posts delivered right to your email.
Pure inspiration, zero spam ✨