Working with SVGs in React Native

Working with SVGs in React Native

Scalable Vector Graphic (SVG) is an image format that uses vector graphics to display the image you see. They are now popular among developers because they scale to any resolution other advantages of SVGs include:

  • Easily Editable: SVG files can be created and edited with any text editor. You can also manipulate the properties with CSS.
  • Small File Size: due to the nature of SVGs the resolutions don’t affect the file sizes allowing the file sizes to be minimal when compared to the pixel-based counterparts
  • SEO Friendly: because of its XML markup language format SVG is SEO friendly as you can add keywords and descriptions to the image directly.
  • They can be animated: We earlier talked about how easy it is to customize and edit SVG files this makes it easy to animate SVG files with CSS and JavaScript

In this article, we will be looking at different ways of manipulating SVG images in React native applications.

Setting up a React Native Project

We will be using the Expo framework to bootstrap our React native application. Make sure you have Node.js/npm on your machine. Open a terminal and run the code below to install the expo-cli globally

npm install -global expo-cli

After installation, run the code below to create a new react-native project

expo install react-native-svg-tutorial

This will launch an interactive prompt for you to select a template. Choose blank and press the enter/return key to continue the installation

Expo installation interactive prompt

Once the process is complete, navigate to the root of the project directory and run the code below to start the expo development server

expo start react-native-svg-tutorial

You will be presented with an interactive menu similar to the screenshot below

Expo development server interactive menu

The Expo dev server allows you to test your application locally while in development. You will need an emulator or the Expo Go App to run the app. The installation of an emulator isn’t covered in this article. Nevertheless, you can check here on how to install an emulator on your computer or the Expo Go app on your mobile device.

Assuming you have an emulator installed, press the relevant key that applies to the emulator, and it’ll run the app on the emulator.

Creating SVG in React Native

We will be creating a custom loader for an application. To start we will need react-native-svg which is a library that provides SVG support for react-native applications. Open a terminal and navigate to the root of your project. Run the code below to install the library

expo install react-native-svg

After installing create a file called Loader.js in the root directory and paste the code below in the file

import * as React from 'react';
import Svg, { Path } from 'react-native-svg';
const Loader = (props) => (
    <Svg width={118} height={107} fill='none' xmlns='http://www.w3.org/2000/svg' {...props}>
        <Path
            d='M32.333 6C17.608 6 5.667 17.819 5.667 32.4c0 11.77 4.666 39.707 50.602 67.947a5.26 5.26 0 0 0 5.462 0c45.936-28.24 50.602-56.176 50.602-67.947 0-14.581-11.941-26.4-26.666-26.4C70.94 6 59 22 59 22S47.059 6 32.333 6Z'
            stroke='#F24E1E'
            strokeWidth={10.667}
            strokeLinecap='round'
            strokeLinejoin='round'
        />
    </Svg>
);
export default Loader;

If you’ve used SVG images on the web this should look familiar. The major difference here is we are using the components provided by react-native-svg for creating our SVG image as opposed to using HTML elements. The react-native-svg components use sentence case in naming to differentiate from the HTML elements which are lower case.

To render our SVG go to App.js file and modify it to look like the code below

import { StatusBar } from 'expo-status-bar';
import { StyleSheet, Text, View } from 'react-native';
import Loader from './Loader';
export default function App() {
    return (
        <View style={styles.container}>
            <Loader />
            <StatusBar style='auto' />
        </View>
    );
}
const styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: '#fff',
    alignItems: 'center',
    justifyContent: 'center',
  },
});

Save, and reload your emulator you should be presented with a screenshot similar to the screenshot below

SVG loader example

Converting an existing SVG image to a React native component can be a chore. Luckily there is an open-source tool called SVGR Playground that let’s you generate a React native component from an SVG.

SVGR Playground

It lets you paste an SVG image on the left and generates a React native SVG component on the right to copy and use. You can go play with it to see how it works.

Open Source Session Replay

OpenReplay is an open-source alternative to FullStory and LogRocket. It gives you full observability by replaying everything your users do on your app and showing how your stack behaves for every issue. OpenReplay is self-hosted for full control over your data.

eplayer.png

Happy debugging for modern frontend teams -start monitoring your web app for free.

Adding External SVG to React native applications

The advantage of creating a React native SVG component is that it is customizable. We can pass props and animate as we will see in the next section, but there are times when we just want to use an external SVG image without having to customize it.

There are two ways to achieve this. The react-native-svg library has a SvgUri component that allows us to create SVG components from external sources. Create a file called SvgExternal.js and paste the code below

import * as React from 'react';
import { SvgUri } from 'react-native-svg';
const SvgExternal = (props) => (
    <SvgUri width={118} height={107} uri='https://placeholder.pics/svg/118x107/DEDEDE/555555/SVG' {...props} />
);
export default SvgExternal;

It provides a uri prop that we can pass a link to an external site. Go to the App.js file and modify the code to display the external SVG.

// previous code
import SvgExternal from './SvgExternal';
export default function App() {
    return (
        <View style={styles.container}>
            <SvgExternal />
            <StatusBar style='auto' />
        </View>
    );
}

// previous code

Now you can open your emulator to view the changes

Another way of achieving this is by using another library called react-native-svg-transformer. It lets you import SVG files into react native projects. Run the code below in your projects terminal to install the library

yarn add --dev react-native-svg-transformer

Create a file called metro.config.js in your project’s root and paste the code below into it.

const { getDefaultConfig } = require("expo/metro-config");

module.exports = (() => {
  const config = getDefaultConfig(__dirname);

  const { transformer, resolver } = config;

  config.transformer = {
    ...transformer,
    babelTransformerPath: require.resolve("react-native-svg-transformer"),
  };
  config.resolver = {
    ...resolver,
    assetExts: resolver.assetExts.filter((ext) => ext !== "svg"),
    sourceExts: [...resolver.sourceExts, "svg"],
  };

  return config;
})();

The metro.config.js is a config file for Metro which is a JavaScript bundler for React native. If you created a react-native project without Expo then paste the code below in your metro.config.js as the configurations for Expo are different from a bare React native app

const { getDefaultConfig } = require("metro-config");

module.exports = (async () => {
  const {
    resolver: { sourceExts, assetExts }
  } = await getDefaultConfig();
  return {
    transformer: {
      babelTransformerPath: require.resolve("react-native-svg-transformer")
    },
    resolver: {
      assetExts: assetExts.filter(ext => ext !== "svg"),
      sourceExts: [...sourceExts, "svg"]
    }
  };
})();

The next step is getting an SVG file. You can download any on the internet or use this. Move the downloaded file to the assets directory in your project. Now go to the App.js file and modify it like the code below to use the file you’ve downloaded.

import { StatusBar } from 'expo-status-bar';
import { StyleSheet, View } from 'react-native';
import PurpleHeart from './assets/purple-heart.svg';
export default function App() {
    return (
        <View style={styles.container}>
            <PurpleHeart />
            <StatusBar style='auto' />
        </View>
    );
}
const styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: '#fff',
    alignItems: 'center',
    justifyContent: 'center',
  },
});

Save when you’re done and view the results in your emulator

External SVG image

Animating SVGs in React Native

We’ve so far looked at different ways of adding Svg to react-native. In this section, we will look at how to animate SVG in react-native. We plan to achieve the screenshot below

Animated SVG

What we want to do is to change the stroke color of the SVG component every few milliseconds. To get started, create a file called AnimatedLoader.js and paste the code below. We will be using the React native Animated API to create the animations.

import React, { useEffect, useRef } from 'react';
import { Animated } from 'react-native';
import Svg, { Path } from 'react-native-svg';
const AnimatedPath = Animated.createAnimatedComponent(Path);
const AnimatedLoader = (props) => {
    const color = useRef(new Animated.Value(0)).current;
    useEffect(() => {
        Animated.loop(
            Animated.sequence([
                Animated.timing(color, { duration: 1000, toValue: 1, useNativeDriver: true }),
                Animated.timing(color, { duration: 1000, toValue: 0, useNativeDriver: true }),
            ])
        ).start();
    }, []);
    return (
        <Svg width={118} height={107} fill='none' xmlns='http://www.w3.org/2000/svg' {...props}>
            <AnimatedPath
                d='M32.333 6C17.608 6 5.667 17.819 5.667 32.4c0 11.77 4.666 39.707 50.602 67.947a5.26 5.26 0 0 0 5.462 0c45.936-28.24 50.602-56.176 50.602-67.947 0-14.581-11.941-26.4-26.666-26.4C70.94 6 59 22 59 22S47.059 6 32.333 6Z'
                stroke={color.interpolate({
                    inputRange: [0, 0.2, 0.4, 0.8, 1],
                    outputRange: [
                        'rgb(147, 189, 186)',
                        'rgb(235, 154, 64)',
                        'rgb(226, 117, 58)',
                        'rgb(220, 85, 52)',
                        'rgb(220, 85, 52)',
                    ],
                })}
                strokeWidth={10.667}
                strokeLinecap='round'
                strokeLinejoin='round'
            />
        </Svg>
    );
};
export default AnimatedLoader;

The first thing we’ve done here is to make the Path component animatable. This allows us to apply animations to the Path component.

const AnimatedPath = Animated.createAnimatedComponent(Path);

The next step is creating an Animated.Value that we can attach to an animated component. We used a useRef hook to store the Animated.Value so we don’t get to mutate it directly. In the useEffect hook, we’ve created our animation which is a loop that creates a fade in and fade out effect.

The last thing to do is hook up the animation with the component that needs it. In this case, the AnimatedPath. We’ve attached the color Animated.Value to the stroke prop of the AnimatedPath. We’re interpolating the color Animated Value, what this does is create a color mapping between the inputRange and outputRange. Save and reload your emulator to view the changes.

Conclusion

We’ve covered how to use and animate SVG’s in react native. When it comes to animating there is so much more that can be achieved that goes beyond the scope of this article. Endeavor to check the React native Animated API. Animating SVG in React native comes with some performance drawbacks especially for complex animations so endeavor to keep it simple. The code of this article is available here.