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

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

majiyd profile image majiyd ・4 min read

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.

Posted on by:

majiyd profile

majiyd

@majiyd

I Love building things

Discussion

pic
Editor guide
 

Awesome tutorial, worked like a charm. thanks!