React Native Series: How to save or share  a React-Native  component as an image
Tags: reactnative, android, ios
Published: 2020-07-11 14:32:28

In this tutorial, I'm going to teach you how to Save/Share a react native component as an image.

**TL/DR** Find the full code in this [github repo](https://github.com/majiyd/save-share-react-native-component-as-image)

I'm assuming you already know how to set up a react native project if you don't, please check out the [official docs](https://facebook.github.io/react-native/docs/getting-started), It's pretty detailed.

Next, let's get rid of the default content of App.js and replace it with this:

```js
// App.js

import React from 'react';
import {
  SafeAreaView,
  StyleSheet,
  ScrollView,
  View,
  Text,
  StatusBar,
  Image,
  TouchableOpacity,
} from 'react-native';

const App = () => {
  return (
    <>
      <StatusBar barStyle="dark-content" />
      <SafeAreaView>
        <ScrollView
          contentInsetAdjustmentBehavior="automatic"
          style={styles.scrollView}>
          <View style={styles.body}>
            <View style={styles.savedComponent}>
              <Text style={styles.text}> Component to be saved </Text>
              <Image
                source={{
                  uri:
                    'https://images.pexels.com/photos/380768/pexels-photo-380768.jpeg?auto=compress&cs=tinysrgb&dpr=2&h=75&w=126',
                }}
                style={styles.image}
              />
              <Text style={styles.text}>Some random text, also saved</Text>
            </View>

            <View style={styles.row}>
              <TouchableOpacity style={styles.button}>
                <Text>Share</Text>
              </TouchableOpacity>
              <TouchableOpacity style={styles.button}>
                <Text>Save</Text>
              </TouchableOpacity>
            </View>
          </View>
        </ScrollView>
      </SafeAreaView>
    </>
  );
};

const styles = StyleSheet.create({
  scrollView: {
    backgroundColor: 'white',
  },
  body: {
    marginTop: 100,
    alignItems: 'center',
  },
  savedComponent: {
    backgroundColor: 'white',
    marginBottom: 30,
  },
  text: {
    textAlign: 'center',
  },
  image: {
    width: 252,
    height: 150,
    alignSelf: 'center',
    marginTop: 30,
    marginBottom: 5,
  },
  row: {
    alignSelf: 'center',
    flexDirection: 'row',
    justifyContent: 'space-around',
    width: '75%',
  },
  button: {
    backgroundColor: '#ad4fcc',
    padding: 15,
    paddingHorizontal: 35,
    borderRadius: 5,
  },
});

export default App;

```
You should see something like this:
![save-or-share-react-native-component-as-image](https://dev-to-uploads.s3.amazonaws.com/i/xniw54aqk7ixlzy4aec9.png)

To achieve our goals, we'll need three packages;
- [React-native-view-shot](https://github.com/gre/react-native-view-shot) - For capturing react-native components
- [React-native-cameraroll](https://github.com/react-native-community/react-native-cameraroll) -  For saving images.
- [React-native-share](https://github.com/gre/react-native-view-shot) -  For sharing images (texts, urls, etc)

Next let's install these packages
```bash
# bash 
yarn add react-native-view-shot @react-native-community/cameraroll react-native-share
```
Now we'll link the packages, 
```bash
# bash 
cd ios && pod install && cd ..
```
Normally auto-linking should work, If auto-linking doesn't work please go to the individual package website and follow the instructions for manual linking.

Next, we have to set permission. To be able to save images we need to gain permission from the user to save images to their device.

For iOS, navigate to `info.plist`
```
project_dir -> ios -> project_name -> info.plist
```
add the following block to `info.plist`
```xml
<!-- info.plist -->

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
...
	<key>NSPhotoLibraryAddUsageDescription</key>
	<string>This app would like to save images to your device.</string>
	<key>NSPhotoLibraryUsageDescription</key>
	<string>This app would like to save images to your device.</string>
...
</dict>
</plist>

```
While for Android, navigate to `AndroidManifest.xml`
```
project_dir -> android -> app -> src -> main -> AndroidManifest.xml
```
Now update `AndroidManifest.xml` like so
```xml
<!-- AndroidManifest.xml -->

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
  package="com.savereactnativecomponent">
    ...
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
    ...
</manifest>
```
Finally, let's implement logic!!!! YAAAAASSSSS!!!!

We create a `ref` for the component we want to save. Also, we'll create a function to handle the image download and bind it to the save button. 

```js
import React, {useRef} from 'react';
...

const App = () => {
  // create a ref
  const viewRef = useRef();

  // get permission on android
  const getPermissionAndroid = async () => {
    try {
      
    } catch (err) {
      // handle error as you please
      console.log('err', err);
    }
  };

  // download image
  const downloadImage = async () => {
    try {
      
    } catch (error) {
      console.log('error', error);
    }
  };

  return (
    <>
      <StatusBar barStyle="dark-content" />
      <SafeAreaView>
        <ScrollView
          contentInsetAdjustmentBehavior="automatic"
          style={styles.scrollView}>
          <View style={styles.body}>
            <View style={styles.savedComponent} ref={viewRef}>
              ...
            </View>

            <View style={styles.row}>
              ...
              <TouchableOpacity style={styles.button} onPress={downloadImage}>
                <Text>Save</Text>
              </TouchableOpacity>
            </View>
          </View>
        </ScrollView>
      </SafeAreaView>
    </>
  );
};

...
export default App;

```
Next we import `captureRef` from `react-native-view-shot`, `captureRef` captures the component and `cameraroll` saves the captured image to the device. 
```js
...
import {
  ...
  PermissionsAndroid,
  Alert,
  Platform,
} from 'react-native';

import {captureRef} from 'react-native-view-shot';
import CameraRoll from '@react-native-community/cameraroll';

const App = () => {
  // create a ref
  const viewRef = useRef();

  // get permission on android
  const getPermissionAndroid = async () => {
    try {
      const granted = await PermissionsAndroid.request(
        PermissionsAndroid.PERMISSIONS.WRITE_EXTERNAL_STORAGE,
        {
          title: 'Image Download Permission',
          message: 'Your permission is required to save images to your device',
          buttonNegative: 'Cancel',
          buttonPositive: 'OK',
        },
      );
      if (granted === PermissionsAndroid.RESULTS.GRANTED) {
        return true;
      }
      Alert.alert(
        '',
        'Your permission is required to save images to your device',
        [{text: 'OK', onPress: () => {}}],
        {cancelable: false},
      );
    } catch (err) {
      // handle error as you please
      console.log('err', err);
    }
  };

  // download image
  const downloadImage = async () => {
    try {
      // react-native-view-shot caputures component
      const uri = await captureRef(viewRef, {
        format: 'png',
        quality: 0.8,
      });

      if (Platform.OS === 'android') {
        const granted = await getPermissionAndroid();
        if (!granted) {
          return;
        }
      }

      // cameraroll saves image
      const image = CameraRoll.save(uri, 'photo');
      if (image) {
        Alert.alert(
          '',
          'Image saved successfully.',
          [{text: 'OK', onPress: () => {}}],
          {cancelable: false},
        );
      }
    } catch (error) {
      console.log('error', error);
    }
  };

  return (
    <>
      ...
    </>
  );
};

...

```
And we're done. Try it out and you'll see that the image will be saved as is demonstrated in the video at the end of this article.

Next, we'll implement sharing, Let's create a function `shareImage` and bind it to the `share` button.

```js
...

import Share from 'react-native-share';

const App = () => {
  ...

  const shareImage = async () => {
    try {
      // capture component 
      const uri = await captureRef(viewRef, {
        format: 'png',
        quality: 0.8,
      });

      // share
      const shareResponse = await Share.open({url: uri});
    } catch (error) {
      console.log('error', error);
    }
  };

  return (
    <>
      <StatusBar barStyle="dark-content" />
      <SafeAreaView>
        <ScrollView
          contentInsetAdjustmentBehavior="automatic"
          style={styles.scrollView}>
          <View style={styles.body}>

            ...

            <View style={styles.row}>
              <TouchableOpacity style={styles.button} onPress={shareImage}>
                <Text>Share</Text>
              </TouchableOpacity>
              ...
            </View>
          </View>
        </ScrollView>
      </SafeAreaView>
    </>
  );
};

...
   

```
And we're done. YAAAAAY!!


In the end, your code should look like this.

{% gist https://gist.github.com/majiyd/cebfdfdc94a5d2d2a7af99bb27f887bf %}

Here's a video demo.

{% youtube YGRy12C5UVk  %}