React Native includes a few options that enable cross-platform apps to select an image from a user's mobile phone gallery or camera and perform further actions on it. One module that does that is called react-native-image-picker.

This module is maintained by the React Native community, which is one reason why I highly recommend that you use this module. It uses native UI to select a photo or a video from the mobile phone's library or the camera of that device.

In this tutorial, let us start with the basics of how to integrate this module for both iOS and Android platforms in a React Native app. We'll then use this module while reviewing some of the common ways to solve different problems when developing an app.

What you need to follow this tutorial

To follow this tutorial, ensure that your dev environment includes the following required packages:

For a complete walkthrough on setting up a development environment for React Native, you can go through the official documentation here.

Create a new React Native app

Start by creating a new React Native app. Open a terminal window and execute the following command. I am going to use npx to access the latest React Native CLI version. After the project directory is created, please make sure that you navigate inside the directory.

npx react-native init rnImagePickerExample

# navigate to project directory
cd rnImagePickerExample

# after navigating inside the directory
# install pods for iOS development

npx pod-install
Thanks to awesome open-source developers at Expo, pod-install is a great package that lets you install CocoaPods essential to building and running react-native apps for iOS. You don't have to dwell inside the ios/ directory and navigate back to the root project. It takes care of that extra step.

Build the app for a specific mobile OS by running either of the two commands as below:

# for iOS
npx react-native run-ios

# for android
npx react-native run-android

Go to the simulator or the device you have connected to run this app and you are going to get a default React Native app.

A default React Native app displayed on an iPhone

Cheers! You have created a new React Native app. Let us now get started with this tutorial.

Install and configure react-native-image-picker

Start by running the below command from a terminal window to install the image picker module.‌‌‌‌

yarn add react-native-image-picker

Once you have done this step, you are going to get a success message in your terminal window. After installing pods, you have to make sure to add permissions. If you do not perform this step, the app might crash or won't work when using react-native-image-picker.

iOS permissions

For iOS, open /ios/rnImagePickerExample/Info.plist and add the following:‌‌

<plist version="1.0">
  <dict>
    // ...
    // add the following
    <key>NSPhotoLibraryUsageDescription</key>
    <string>$(PRODUCT_NAME) would like access to your photo gallery</string>
    <key>NSCameraUsageDescription</key>
    <string>$(PRODUCT_NAME) would like to use your camera</string>
    <key>NSPhotoLibraryAddUsageDescription</key>
    <string>$(PRODUCT_NAME) would like to save photos to your photo gallery</string>
    <key>NSMicrophoneUsageDescription</key>
    <string>$(PRODUCT_NAME) would like to use your microphone (for videos)</string>
  </dict>
</plist>

This is an essential step to make sure you ask for the right permission with the right message, especially if you are looking forward to publishing your app on the store.

CocoaPods installation

Another necessary step to make this app run on iOS is to install CocoaPods for the image picker library we have just installed. After the release of React Native 0.60, the team behind the framework introduced auto-linking so we do not require to link the library. This is "auto-linking" is done by installing pods.

npx pod-install

Make sure to run the build command mentioned in the previous section to run the app again after this step.

Android permissions

Similarly, for Android, you have to add the required permissions in the file /android/app/src/main/AndroidManifest.xml. The first permission is to access the device's camera, and the second permission is to read or write to storage. This second option for the current demo app allows us to choose the image from the device's image gallery.

<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>

That's it to install and configure an image picker library in a react native app. For more information check out the official doc here.

Create a simple image picker screen component

The advantage of the React Native Image Picker library is the number of options it provides for you to set and use in your app. The simplest options include changing the title of the picker button or the cancel button, or setting the max height and width of the image picked, but there are some advanced options such as skipBack that are useful when uploading or communicating with a backend service.

Start by creating a simple image picker screen component. Make sure you are at the root location of your project to follow these steps.

  • Create a new directory called src.
  • Inside src create a new sub-directory to store all screen components called screens. This is a common convention to keep all screen components inside a React Native app.
  • Inside the sub-directory create the screen component file called SimpleImagePicker.js as well as a global a style file called Styles.js.

Inside the Styles.js let us define some basic styles using the StyleSheet object from the React Native core API. In the snippet below, you are also going to find some hex color codes that are going to be used throughout this tutorial. Feel free to change these values as per your preference.

// Global shared styles for screens

import { StyleSheet } from 'react-native';

export const STYLES = StyleSheet.create({
  flex: {
    flex: 1
  },
  centerContainer: {
    alignItems: 'center',
    justifyContent: 'center'
  },
  title: {
    fontSize: 22
  }
});

export const COLORS = {
  primaryDark: '#22212c',
  primaryLight: '#f8f8f2',
  primaryRed: '#ff5555',
  primaryPink: '#ff80bf',
  primaryYellow: '#ffff80',
  primaryOrange: '#ff9580'
};

Typically you define the types of styles in the above code snippet for a particular screen. But if you are planning to have more than one screen component, you can share styles by creating a global file.

Next, inside SimpleImagePicker.js, start by importing necessary statements as well as styles that you have just created.

Then create a basic component that displays a <Text> inside the functional component SimpleImagePicker().

import React, { useState } from 'react';
import { View, Text } from 'react-native';
import { STYLES, COLORS } from './Styles';

export default function SimpleImagePicker() {
  const [imageSource, setImageSource] = useState(null);
  return (
    <View
      style={[
        STYLES.flex,
        STYLES.centerContainer,
        { backgroundColor: COLORS.primaryDark }
      ]}
    >
      <Text style={[STYLES.title, { color: COLORS.primaryLight }]}>
        Simple Image Picker
      </Text>
    </View>
  );
}

The state variable imageSource in the above snippet is going to store the value of the URI or the source of the image on the device when a user chooses an image.

To see this screen component in action, one last step left is to modify the App.js file as shown below.

import React from 'react';
import { StatusBar } from 'react-native';
import SimpleImagePicker from './src/screens/SimpleImagePicker';

export default function () {
  return (
    <>
      <StatusBar barStyle='light-content' />
      <SimpleImagePicker />
    </>
  );
}

Go back to a simulator or the physical device you are using to run this app and you are going to be welcomed by the following screen.

An app screen with the text "Simple Image Picker"

How to pick an image?

In this section, let's create a handler method that is going to allow the user to pick an image. To start, make sure you update the import statements by importing core components from React Native such as TouchableOpacity and Alert. Also, import ImagePicker from the react-native-image-picker library.

Then inside, the screen component creates a new handler method called selectImage(). This method is going to trigger a button on the screen when the user wants to select an image from the device's library from <TouchableOpacity>.

Inside this method, you are going to add an options object with some primary options like the title of the image picker, the maximum width and height of an image, and setting storage options to not backup an image selected. This options object is used for customization and its not mandatory. This object is also passed as the first argument to the method that actually selects an image.

ImagePicker library has a method called showImagePicker which accepts an object called response as the second argument. This method's only job is to display the image picker. It shows an alert box when the button is pressed to select an image.

The response object is the most important. It has properties to determine when the user cancels the process of picking an image from the library, or when there is an error in the process of picking an image, or the URI of the local file asset selected by the user, and so on.

We are going to use some of the basic options like did cancel, error, customButton, and uri.

Modify the screen component file as shown below.

import React, { useState } from 'react';
import { View, Text, TouchableOpacity, Alert } from 'react-native';
import { STYLES, COLORS } from './Styles';
import ImagePicker from 'react-native-image-picker';

export default function SimpleImagePicker() {
  const [imageSource, setImageSource] = useState(null);

  function selectImage() {
    let options = {
      title: 'You can choose one image',
      maxWidth: 256,
      maxHeight: 256,
      storageOptions: {
        skipBackup: true
      }
    };

    ImagePicker.showImagePicker(options, response => {
      console.log({ response });

      if (response.didCancel) {
        console.log('User cancelled photo picker');
        Alert.alert('You did not select any image');
      } else if (response.error) {
        console.log('ImagePicker Error: ', response.error);
      } else if (response.customButton) {
        console.log('User tapped custom button: ', response.customButton);
      } else {
        let source = { uri: response.uri };
        console.log({ source });
      }
    });
  }

  return (
    <View
      style={[
        STYLES.flex,
        STYLES.centerContainer,
        { backgroundColor: COLORS.primaryDark }
      ]}
    >
      <Text style={[STYLES.title, { color: COLORS.primaryLight }]}>
        Simple Image Picker
      </Text>
      <TouchableOpacity
        onPress={selectImage}
        style={[
          STYLES.selectButtonContainer,
          { backgroundColor: COLORS.primaryLight }
        ]}
      >
        <Text style={STYLES.selectButtonTitle}>Pick an image</Text>
      </TouchableOpacity>
    </View>
  );
}

Also, modify the Styles.js file and add the styles for the new button you have just created.

export const STYLES = StyleSheet.create({
  flex: {
    flex: 1
  },
  centerContainer: {
    alignItems: 'center',
    justifyContent: 'center'
  },
  title: {
    fontSize: 22
  },
  // add below
  selectButtonContainer: {
    margin: 20,
    borderRadius: 5
  },
  selectButtonTitle: {
    padding: 10,
    fontSize: 18
  }
});

Now go back to the simulator and you are going to see the new button we have just added is being displayed.

The app now includes a "Pick an image" button

Click the button and there is going to be an alert box pop up.

An alert box prompts you to take a photo or choose from your library

It gives you the option to select an image from the library or take a photo from the device's camera (since I am using a simulator, taking a photo is not possible).

At this moment, if you press Cancel, you are going to see another alert box (that we intentionally added to the screen component).

A prompt warns you that "You did not select any image"

The console statements are also being logged correctly by the metro bundler terminal window. You can verify that as shown in the image below.

The terminal window logs the user's activity

To make the alert box disappear, click the button OK. Click the UI button to select an image again, and this time, choose an image from the library.

The first thing it is going to do is to ask for permission. This only happens for the first time when the user is using the app.

A prompt requests permissions to access media

Next, select an image in your library as shown below.

A photo select screen
Enter the photo folder to find your image

After you have selected the image, nothing happens. This is because we still have to write the code to display the image. For now, go to the terminal window and you are going to the response object as well source object with data and information about the image.

If it is not visible or understandable, take a look at the JSON snippet below.

{
  "response": {
    "data": "/9j/4AAQSkZJRgABAQAAAQABAAD/4gIoSUNDX1BST0ZJTEUAAQEAAAIYAAAAAAIQAABtbnRyUkdCIFhZWiAAAAAAAAAAAAAAAABhY3NwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAA9tYAAQAAAADTLQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAlkZXNjAAAA8AAAAHRyWFlaAAABZAAAABRnWFlaAAABeAAAABRiWFlaAAABjAAAABRyVFJDAAABoAAAAChnVFJDAAABoAAAAChiVFJDAAABoAAAACh3dHB0AAAByAAAABRjcHJ0AAAB3AAAADxtbHVjAAAAAAAAAAEAAAAMZW5VUwAAAFgAAAAcAHMAUgBHAEIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFhZWiAAAAAAAABvogAAOPUAAAOQWFlaIAAAAAAAAGKZAAC3hQAAGNpYWVogAAAAAAAAJKAAAA+EAAC2z3BhcmEAAAAAAAQAAAACZmYAAPKnAAANWQAAE9AAAApbAAAAAAAAAABYWVogAAAAAAAA9tYAAQAAAADTLW1sdWMAAAAAAAAAAQAAAAxlblVTAAAAIAAAABwARwBvAG8AZwBsAGUAIABJAG4AYwAuACAAMgAwADEANv/bAEMAAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAf/bAEMBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAf/AABEIAQABAAMBIgACEQEDEQH/xAAVAAEBAAAAAAAAAAAAAAAAAAAAC//EABQQAQAAAAAAAAAAAAAAAAAAAAD/xAAUAQEAAAAAAAAAAAAAAAAAAAAA/8QAFBEBAAAAAAAAAAAAAAAAAAAAAP/aAAwDAQACEQMRAD8An/gAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA//2Q==",
    "fileName": "image-4be54ecf-83c7-4819-bf37-b549cd3efa71.jpg",
    "fileSize": 1222,
    "height": 256,
    "isVertical": true,
    "originalRotation": 0,
    "path": "/storage/emulated/0/Pictures/image-4be54ecf-83c7-4819-bf37-b549cd3efa71.jpg",
    "type": "image/jpeg",
    "uri": "file:///storage/emulated/0/Pictures/image-4be54ecf-83c7-4819-bf37-b549cd3efa71.jpg",
    "width": 256
  }
}

The response object returns details related to the image such as:

  • data: base64 encoded image data generated
  • fileSize: the size of the file
  • fileName: the name of the file name
  • type: media type such as image/jpeg
  • uri: which is also the value of the source object
  • the width and height but with dimensions that we passed as the properties of options object and not the actual image size (which is 500px x 500px).

You can stop the base64 data generation of the image by adding another property called noData: true in the options object, and it will stop generating the base64 if there is no requirement.

let options = {
  // rest of the properties remain same
  noData: true
};

After adding this property, the new response object won't have a data field.

The type of the source is essential for the image picker to know. For example, if you only want the user to select an image instead of a video file, then you can set another property called mediaType in the options object.

let options = {
  // rest of the properties remain same
  mediaType: 'photo' // other values 'video', 'mixed'
};

Setting the value to mixed for this property type will allow the user to select either an image or a video. There are other options on how you can control the user's input when selecting an image or a video that you can find in the official documentation here of the image picker library.

Difference between launchImageLibrary() and showImagePicker()

There are separate functions provided by ImagePicker to use for selected methods:

  • launchCamera(options?, (response) => {})
  • launchImageLibrary(options?, (response) => {})

Using these methods eliminates the need to show the below alert box to select an option when choosing an image:

You can bypass the image selection prompt from before

These methods are for a direct use case such as when you only want the user to select the image either from the device's library or take a photo from the device's camera. If you want to give the user only one option from these two, then you should use one of these methods.

These methods work similarly to showImagePicker. Let's try one of them. Replace the method showImagePicker() with launchImageLibrary() in the screen component file.

ImagePicker.launchImageLibrary(options, response => {
  console.log({ response });

  if (response.didCancel) {
    console.log('User cancelled photo picker');
    Alert.alert('You did not select any image');
  } else if (response.error) {
    console.log('ImagePicker Error: ', response.error);
  } else if (response.customButton) {
    console.log('User tapped custom button: ', response.customButton);
  } else {
    let source = { uri: response.uri };
    console.log({ source });
  }
});

Now go back to the device and you'll notice only one difference. The image library opens as soon as the button from the app is clicked.

The app now bypasses the prompt

The rest of the process is going to be the same.

Display the image

In this section, let's complete the demo app by displaying the image when the user picks an image from the library. We are also going to display a placeholder image when no image is being selected by the user.

To start, you are going to import the Image from the React Native core API. Then update the handler function by setting the source URI of the image to the state variable image source we defined in the earlier section.

Lastly, you update the JSX of the component.

Here is the complete code snippet for the SimpleImagePicker screen component. We are going to conditionally render when to show the placeholder image from a local asset file and when to display the image that the user has picked.

import React, { useState } from 'react';
import { View, Text, TouchableOpacity, Alert, Image } from 'react-native';
import { STYLES, COLORS } from './Styles';
import ImagePicker from 'react-native-image-picker';

export default function SimpleImagePicker() {
  const [imageSource, setImageSource] = useState(null);

  function selectImage() {
    let options = {
      title: 'You can choose one image',
      maxWidth: 256,
      maxHeight: 256,
      noData: true,
      mediaType: 'photo',
      storageOptions: {
        skipBackup: true
      }
    };

    ImagePicker.launchImageLibrary(options, response => {
      if (response.didCancel) {
        console.log('User cancelled photo picker');
        Alert.alert('You did not select any image');
      } else if (response.error) {
        console.log('ImagePicker Error: ', response.error);
      } else if (response.customButton) {
        console.log('User tapped custom button: ', response.customButton);
      } else {
        let source = { uri: response.uri };

        // ADD THIS
        setImageSource(source.uri);
      }
    });
  }

  return (
    <View
      style={[
        STYLES.flex,
        STYLES.centerContainer,
        { backgroundColor: COLORS.primaryDark }
      ]}
    >
      <Text style={[STYLES.title, { color: COLORS.primaryLight }]}>
        Simple Image Picker
      </Text>
      {/* ADD THIS */}
      <View style={STYLES.imageContainer}>
        {imageSource === null ? (
          <Image
            source={require('../assets/placeholderimage.jpg')}
            style={STYLES.imageBox}
            resizeMode='contain'
          />
        ) : (
          <Image
            source={{ uri: imageSource }}
            style={STYLES.imageBox}
            resizeMode='contain'
          />
        )}
      </View>
      <TouchableOpacity
        onPress={selectImage}
        style={[
          STYLES.selectButtonContainer,
          { backgroundColor: COLORS.primaryLight }
        ]}
      >
        <Text style={STYLES.selectButtonTitle}>Pick an image</Text>
      </TouchableOpacity>
    </View>
  );
}

Also, update the Styles.js file:

export const STYLES = StyleSheet.create({
  // rest of the styles remain same
  // ADD BELOW
  imageContainer: {
    marginVertical: 20,
    borderWidth: 5,
    borderColor: '#ff5555'
  },
  imageBox: {
    width: 256,
    height: 256
  }
});

Go back to the app and when an image is not picked, you are going to see the following result.

A placeholder image appears

When an image is selected, it is going to be displayed instead of the placeholder image.‌‌

A basic image has been selected

Conclusion

In this post, you have learned how to configure and implement the react-native-image-picker library to allow a user to pick an image from their device library. When on a physical device, try testing the above code snippet using the device's Camera. The core functionalities discussed in this post can be customized and advanced further to add more functionalities.

Here is the link to the complete API for the react-native-image-picker library.

The complete code for this tutorial is available at this GitHub repo here.

For more advanced usage of this library with a Crowdbotics app, check out our following tutorials:


❤️ For more tutorials on React Native and Firebase you can check my personal blog here.