Creating an SVG Icon System in React Native

One of React Native’s benefits is allowing web front-end developers to use their existing skillset to build native apps. However, just because the base technology is similar, the manner in which you need to implement existing web techniques can be quite different — and an icon system is just one example.

React Native has no native support for SVGs (in fact, AFAIK, you can’t really use SVGs in proper native iOS development), but thankfully, the React Native Community has created a fully-featured SVG library for React Native.

react-native-svg allows you draw with inline SVG similarly to how we would on the web, but I expect most developers would like to use it simply to display icons from an existing SVG <use> icon system.

So I have recently written and published react-native-svg-icon, which leverages the aforementioned library to make using an SVG icon very easy.

Creating our Icon System

Installation

1. Install rpm if it’s not already:

npm install rnpm -g

2. Install react-native-svg:

npm install react-native-svg --save

3. Link it to your project:

rnpm link react-native-svg

4. Install react-native-svg-icon:

npm install react-native-svg-icon --save

Setting up the Icon System

We need to convert our existing, web-friendly SVG icon system for use in React Native. I may end up writing a plugin to do this automatically, but in the meantime we can do it with good ol’ fashioned Find and Replace.

An existing <symbol /> like this:

<symbol id="pencil" viewbox="0 0 100 100"><path d="M24 91.537l5.99-6.12-15.87-15.495L8 75.912V83h9v8h7.162H24v.537zm34.464-61.329c0-.954-.521-1.432-1.563-1.432-.434 0-.781.174-1.042.52L20.052 64.975c-.26.347-.39.738-.39 1.172 0 .955.477 1.432 1.432 1.432.434 0 .781-.13 1.041-.39L57.943 31.38c.347-.347.52-.738.52-1.172zm-3.646-12.63l27.474 27.474L27.344 100H0V72.526l54.818-54.948zM100 23.958c0 2.257-.825 4.21-2.474 5.86L86.588 40.755 59.115 13.281 70.052 2.474C71.615.824 73.568 0 75.912 0c2.343 0 4.383.825 6.12 2.474l15.494 15.364c1.65 1.737 2.474 3.777 2.474 6.12z"/></symbol>

Needs to be converted to:

Pencil: <Path d="M24 91.537l5.99-6.12-15.87-15.495L8 75.912V83h9v8h7.162H24v.537zm34.464-61.329c0-.954-.521-1.432-1.563-1.432-.434 0-.781.174-1.042.52L20.052 64.975c-.26.347-.39.738-.39 1.172 0 .955.477 1.432 1.432 1.432.434 0 .781-.13 1.041-.39L57.943 31.38c.347-.347.52-.738.52-1.172zm-3.646-12.63l27.474 27.474L27.344 100H0V72.526l54.818-54.948zM100 23.958c0 2.257-.825 4.21-2.474 5.86L86.588 40.755 59.115 13.281 70.052 2.474C71.615.824 73.568 0 75.912 0c2.343 0 4.383.825 6.12 2.474l15.494 15.364c1.65 1.737 2.474 3.777 2.474 6.12z"/>,

As you can see, the <symbol tag and viewbox attributes are gone. The id is now a TitleCase object key, and <path> is changed to <Path>. In React, all custom elements need to begin wth a capital letter, and hyphenated attributes are camelCase, so in gradients, stop-color would be stopColor. Bucking the trend, however, viewBox is also camelCased, and not doing so will result in:

'Prop `viewbox` is deprecated. please use `viewBox` instead.'

Alternatively, your SVG can itself be an object with svg and viewBox properties, so you don’t have to specify it in every component instance should it be different than the default.

Pencil: {
svg: <Path d="M24 91.537l5.99-6.12-15.87-15.495L8 75.912V83h9v8h7.162H24v.537zm34.464-61.329c0-.954-.521-1.432-1.563-1.432-.434 0-.781.174-1.042.52L20.052 64.975c-.26.347-.39.738-.39 1.172 0 .955.477 1.432 1.432 1.432.434 0 .781-.13 1.041-.39L57.943 31.38c.347-.347.52-.738.52-1.172zm-3.646-12.63l27.474 27.474L27.344 100H0V72.526l54.818-54.948zM100 23.958c0 2.257-.825 4.21-2.474 5.86L86.588 40.755 59.115 13.281 70.052 2.474C71.615.824 73.568 0 75.912 0c2.343 0 4.383.825 6.12 2.474l15.494 15.364c1.65 1.737 2.474 3.777 2.474 6.12z"/>,
viewBox: '0 0 50 50'
}

Just like with web icon systems, omitting fill attributes on shapes will allow us to color them later on.

When you have converted all of your SVGs, create an svgs.js in your application, which imports the necessary SVG features from react-native-svg, and exports an object of SVG icon names and their shapes. It should look similar to this:

import React from 'react';
import { Defs, G, LinearGradient, Path, Rect, Stop } from 'react-native-svg'; // import whichever features are used in your SVGs
export default {
IconName1: <Path />,
IconName2: <G><Path /><Path /></G>,
Etc: <Etc />
}

Note: I recommend using minified SVGs to keep your svg.js file as small as possible. Alternatively, you could have a file for each icon, and import them all and combine them (with Object.assign()) in to one object. Use whatever works best for your situation.

Creating the <Icon /> Component

Create an Icon.js, which consists of:

import React from 'react';
import SvgIcon from 'react-native-svg-icon';
import svgs from './assets/svgs'; // point to your svgs.js wherever that may be
const Icon = (props) => <SvgIcon {...props} svgs={svgs} />;
export default Icon;

Out of the box, the defaultProps for <SvgIcon /> are:

{
fill: '#000', // Inoffensive, but probably inappropriate for most
height: '44', // iOS HIGs' recommended size for touchable icons
width: '44',
viewBox: '0 0 100 100' // It's a nice canvas size to work with
}

You can of course override these defaults in your <Icon /> component, or on a per-use basis.

To override the defaults, you would simply add to Icon.js with the necessary overrides:

Icon.defaultProps = {
fill: '#fff',
viewBox: '0 0 50 50'
};
// And set `propType` validation for completeness. All props expect `string`s

Using your <Icon />s

As with any component, you need to import it where you’ll use it.

import Icon from './Icon'; // point to your Icon.js location

Then use it in a component’s render() method:

export default class SvgIconSystem extends Component {
render() {
return (
<View style={styles.container}>
<Icon name="SortArrows" />
<Icon name="Magnifier" fill="blue" />
<Icon name="Pencil" fill="red" width="20" height="20" />
<Icon name="LogoRss" />
<Icon name="LogoTwitterSpaced" width="177" height="108" viewBox="0 0 348 216" />
</View>
);
}
}

Each <Icon />'s name, fill color, width, height and viewBox can be set with props to render them how you need.

And here’s the final output!
Borders for illustration purposes only

Screenshot of rendered SVG icon system on an iPhone 6

So that’s it! You now know how to create an SVG icon system in React Native \o/

If you find any bugs or have feature requests, please log an issue on GitHub.

This article has been cross-posted on Codepen for better code formatting.