In my experience working with React Native, one of the tasks Iβve found myself doing most often is building the functionality for uploading images from user devices. This can be challenging if youβve never done it before.
In this tutorial, weβll demonstrate how to use React Native Image Crop Picker to enable your users to select images from their devices or use the deviceβs camera to capture and upload live photos to your app.
To show how react-native-image-crop-picker
works, weβll create a reusable image picker component that handles permission to select an image from the media library or take a new image using the camera.
By the end of the tutorial, you should use the image picker component like so:
import * as React from 'react';
import {View} from 'react-native';
import {ImageOrVideo} from 'react-native-image-crop-picker';
import {Avatar} from './Avatar';
export const Profile = () => {
const onAvatarChange = (image: ImageOrVideo) => {
console.log(image);
// upload image to server here
};
return (
<Avatar
onChange={onAvatarChange}
source={require('./avatar-placeholder.png')}
/>
);
};
Hereβs a simple demo of the finished result:
There are two popular libraries you can use to implement the image picker component:react-native-image-picker
and react-native-image-crop-picker
. React Native Image Picker is another React Native module for selecting media from the device library or camera. So why use React Native Image Crop Picker?
The advantage to using react-native-image-crop-picker
is that, unlike react-native-image-picker
, it enables you to crop and compress images. This is especially critical when building iOS apps because uploading large files could introduce performance issues. Compressing and cropping images is also helpful for users with low internet speed.
react-native-image-crop-picker
Letβs create a new React Native project using a TypeScript template:
npx react-native init ImagePickerDemo --template react-native-template-typescript
Next, install react-native-image-crop-picker
:
yarn add react-native-image-crop-picker
Because react-native-image-crop-picker
comes with some native dependencies, we need to install pod
and rebuild the app:
cd ios && pod install && cd ..
To use react-native-image-crop-picker
, you should add the following config to info.plist
:
<key>NSPhotoLibraryUsageDescription</key>
<string>$(PRODUCT_NAME) would like to upload photos from your photo gallery</string>
<key>NSCameraUsageDescription</key>
<string>$(PRODUCT_NAME) requires to access camera for uploading photos to your profile or posts</string>
<key>NSMicrophoneUsageDescription</key>
<string>$(PRODUCT_NAME) requires to access Audio recording to record and uplod videos</string>
The NSPhotoLibraryUsageDescription
config describes why you need access to user photos and why youβre accessing the camera. If you only want to access images from the device library, you donβt need to add the NSMicrophoneUsageDescription
or NSCameraUsageDescription
keys.
Now weβre ready to build and open the app:
yarn ios
The Android configuration for react-native-image-crop-picker
is as follows.
First, add useSupportLibrary
(android/app/build.gradle
):
android {
...
defaultConfig {
...
vectorDrawables.useSupportLibrary = true
...
}
...
}
If you want to use the camera picker in your project, add the following to app/src/main/AndroidManifest.xml
:
<uses-permission android:name="android.permission.CAMERA"/>
If you want to allow the user to use their front camera, you should also add the following to app/src/main/ AndroidManifest.xml
:
<uses-feature android:name="android.hardware.camera" android:required="false" />
<uses-feature android:name="android.hardware.camera.front" android:required="false" />
Itβs important to mention that you need to upgrade to Android SDK 26+. If youβre using React Native 0.64, youβre safe. If thatβs not the case, make sure to updateandroid/app/build.gradle
:
android {
compileSdkVersion 27
buildToolsVersion "27.0.3"
...
defaultConfig {
...
targetSdkVersion 27
...
}
...
}
In this section, weβll build a simple screen to update the user avatar. The idea is to create an image picker component to allow the user to upload a new avatar. The avatar component should extend its prop
from the Image
component, and weβll add onChange
to handle uploading a new image from the user device.
Weβll wrap our image with a pressable component to open the photo library when the user presses their profile picture. Opening the image library is as easy as calling the openPicker
function from react-native-image-crop-picker
.
Letβs add a crop feature that allows users to crop the selected image before uploading:
import React from 'react';
import {Image, ImageProps, StyleSheet, TouchableOpacity} from 'react-native';
import ImagePicker, {ImageOrVideo} from 'react-native-image-crop-picker';
interface AvatarProps extends ImageProps {
onChange?: (image: ImageOrVideo) => void;
}
export const Avatar = (props: AvatarProps) => {
const [uri, setUri] = React.useState(props.source?.uri || undefined);
const pickPicture = () => {
ImagePicker.openPicker({
width: 300,
height: 400,
cropping: true,
}).then(image => {
setUri(image.path);
props.onChange?.(image);
});
};
return (
<TouchableOpacity onPress={pickPicture}>
<Image
style={styles.avatar}
{...props}
source={uri ? {uri} : props.source}
/>
</TouchableOpacity>
);
};
const styles = StyleSheet.create({
avatar: {
paddingTop: 20,
height: 100,
width: 100,
borderRadius: 100,
padding: 20,
},
});
Now that our avatar component is ready to be used, letβs create a profile screen and update the userβs profile picture:
import * as React from 'react';
import {StatusBar, StyleSheet, View} from 'react-native';
import {ImageOrVideo} from 'react-native-image-crop-picker';
import {Avatar} from './Avatar';
import {UserInfo} from './UserInfo';
export const Profile = () => {
const onAvatarChange = (image: ImageOrVideo) => {
console.log(image);
// upload image to server here
};
return (
<View style={styles.scroll}>
<StatusBar barStyle="dark-content" />
<View style={styles.userRow}>
<Avatar
onChange={onAvatarChange}
source={require('./avatar-placeholder.png')}
/>
<UserInfo />
</View>
<View style={styles.content} />
</View>
);
};
const styles = StyleSheet.create({
scroll: {
backgroundColor: 'white',
flex: 1,
},
userRow: {
alignItems: 'center',
padding: 15,
marginTop: 70,
},
content: {
flex: 1,
backgroundColor: '#d8d8db',
},
});
Here we added the onChange
prop to the avatar. This will provide us with all the file info, which we can easily use to upload it to our server. Here is the result:
Letβs say we want to give the user the ability to take a photo from their camera and upload it. We should also give the user the option to select a photo from the library or use their device camera to take a new photo.
Note that openCamera
wonβt work on iOS Simulator. To test it, you should run the app on a real device.
To use the camera, we only need to call the openCamera
function instead of openPicker
:
import React from 'react';
import {
Image,
ImageProps,
Pressable,
SafeAreaView,
StyleSheet,
Text,
TouchableOpacity,
} from 'react-native';
import ImagePicker, {ImageOrVideo} from 'react-native-image-crop-picker';
import Modal from 'react-native-modal';
import {CameraIcon, ImageIcon} from './icons';
interface AvatarProps extends ImageProps {
onChange?: (file: ImageOrVideo) => void;
}
export const Avatar = (props: AvatarProps) => {
const [uri, setUri] = React.useState(props.source?.uri || undefined);
const [visible, setVisible] = React.useState(false);
const close = () => setVisible(false);
const open = () => setVisible(true);
const chooseImage = () => {
ImagePicker.openPicker({
width: 300,
height: 400,
cropping: true,
})
.then(image => {
setUri(image.path);
props.onChange?.(image);
})
.finally(close);
};
const openCamera = () => {
ImagePicker.openCamera({
width: 300,
height: 400,
cropping: true,
})
.then(image => {
setUri(image.path);
props.onChange?.(image);
})
.finally(close);
};
return (
<>
<TouchableOpacity onPress={chooseImage}>
<Image
style={styles.avatar}
{...props}
source={uri ? {uri} : props.source}
/>
</TouchableOpacity>
<Modal
isVisible={visible}
onBackButtonPress={close}
onBackdropPress={close}
style={{justifyContent: 'flex-end', margin: 0}}>
<SafeAreaView style={styles.options}>
<Pressable style={styles.option} onPress={chooseImage}>
<ImageIcon />
<Text>Library </Text>
</Pressable>
<Pressable style={styles.option} onPress={openCamera}>
<CameraIcon />
<Text>Camera</Text>
</Pressable>
</SafeAreaView>
</Modal>
</>
);
};
const styles = StyleSheet.create({
avatar: {
paddingTop: 20,
height: 100,
width: 100,
borderRadius: 100,
padding: 20,
},
options: {
backgroundColor: 'white',
flexDirection: 'row',
borderTopRightRadius: 30,
borderTopLeftRadius: 30,
},
option: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
},
});
Hereβs a demo of our finished React Native app:
Now you can copy-paste this image picker component whenever you need to build an image upload feature in a React Native app.
The full code is available in this Github repo if you want to play around with this project.
LogRocket is a React Native monitoring solution that helps you reproduce issues instantly, prioritize bugs, and understand performance in your React Native apps.
LogRocket also helps you increase conversion rates and product usage by showing you exactly how users are interacting with your app. LogRocket's product analytics features surface the reasons why users don't complete a particular flow or don't adopt a new feature.
Start proactively monitoring your React Native apps β try LogRocket for free.
Hey there, want to help make our blog better?
Join LogRocketβs Content Advisory Board. Youβll help inform the type of content we create and get access to exclusive meetups, social accreditation, and swag.
Sign up nowLearn how to implement one-way and two-way data binding in Vue.js, using v-model and advanced techniques like defineModel for better apps.
Compare Prisma and Drizzle ORMs to learn their differences, strengths, and weaknesses for data access and migrations.
It’s easy for devs to default to JavaScript to fix every problem. Let’s use the RoLP to find simpler alternatives with HTML and CSS.
Learn how to manage memory leaks in Rust, avoid unsafe behavior, and use tools like weak references to ensure efficient programs.
2 Replies to "How to build an image picker using react-native-image-crop-picker"
import {Avatar} from ‘./Avatar’;
define Avater
app is getting crashed, when image is clicked on camera and when saved in android. Getting this error
Caused by: java.lang.IllegalStateException: Screen fragments should never be restored. Follow instructions from https://github.com/software-mansion/react-native-screens/issues/17#issuecomment-424704067 to properly configure your main activity
androidx.fragment.app.Fragment$InstantiationException: Unable to instantiate fragment com.swmansion.rnscreens.ScreenStackFragment: calling Fragment constructor caused an exception