DEV Community

Cover image for React Native Series: How to save or share  a React-Native  component as an image
majiyd
majiyd

Posted on

React Native Series: How to save or share a React-Native component as an image

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

I'm assuming you already know how to set up a react native project if you don't, please check out the official docs, It's pretty detailed.

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

// 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

To achieve our goals, we'll need three packages;

Next let's install these packages

# bash 
yarn add react-native-view-shot @react-native-community/cameraroll react-native-share

Now we'll link the packages,

# 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

<!-- 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

<!-- 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.

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.

...
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.

...

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.

Here's a video demo.

Discussion (5)

Collapse
mpeyper profile image
Michael Peyper

Exactly what I needed, thank you so much!

Just a head's up that the link in the article to react-native-share is incorrect (it goes to react-native-view-shot instead) and the correct link is github.com/react-native-share/reac....

Collapse
guillermodv profile image
guillermodv

I am going to use it today! thanks!

Collapse
wjimmycook profile image
Jimmy Cook

Awesome tutorial, worked like a charm. thanks!

Collapse
majiyd profile image
majiyd Author

Glad It did

Collapse
stephencavender profile image
Stephen Cavender

Great tutorial, thank you!

fyi the link to react-native-share goes to react-native-view-shot