Using SVG Icons Components in React

Nishan Bajracharya
Leapfrog
Published in
5 min readMar 13, 2018

--

SVGs are cool. They scale up. They scale down. Sometimes they try to kill you in your sleep but you know, necessary evil and stuff.

So what’s good about SVG?

SVG or Scalable Vector Graphics are XML-based image formats that can be scaled to any size while maintaining the quality of the image. So when you need an image that needs to be scaled as big or as small as you want, SVGs are the way to go. They’re basically XML documents so their file sizes also tend to be tiny compared to other image formats.

Also they are effectively XML elements and can be manipulated using CSS. So changing colors and strokes on SVG can be done all via CSS.

Sounds good. What about the bad stuff?

When it comes to images, SVGs are great for simple shapes, full of basic strokes and color. Anything more complex than icons though and they are not worth the hassle. (Unless you’re doing data visualizations, in which case, let me point you towards D3.js.)

It is also more complicated to build SVGs on your own. Since they are structured in XML, building one can be harder than an equivalent raster image, which stores pixels data.

Scaling — Raster vs Vector

Where does React come into all this?

When using SVG in a web document, you have two options. Either render the SVG document as is, or use it as source in the img tag. The preferable option is to use it as is, since SVG in the image tag is rendered as an image and cannot be manipulated beyond the css styles for the image tag.

So when deciding on using SVG in a React project, it is preferable to build a component than render the SVG into the document.

const SVG = () => 
<svg
width="100%"
height="100%"
viewBox="0 0 32 32"
xmlns="http://www.w3.org/2000/svg"
xmlnsXlink="http://www.w3.org/1999/xlink"
>
<path d="some path here" fill="#000" />
</svg>;

This would render a static SVG into the html document. Let’s add some props.

const SVG = ({
style = {},
fill = '#fff',
width = '100%',
className = '',
height = '100%',
viewBox = '0 0 32 32',
}) =>
<svg
width={width}
style={style}
height={height}
viewBox={viewBox}
className={className}
xmlns="http://www.w3.org/2000/svg"
xmlnsXlink="http://www.w3.org/1999/xlink"
>
<path d="some path here" fill={fill} />
</svg>;

We can now use this component to render SVG of different colors, classnames and styles. Check out a CodeSandbox demonstration below.

An example of a basic SVG component

What about when we have multiple icons to deal with?

OK, so we now have a general idea of how we can create React components for SVG icons. How do we deal with a large plethora of icons then, which is quite common in large projects? Here we have multiple options to go for. We could have a giant component which returns the required SVG icon or create a mapper component that takes in a prop and maps it to the equivalent SVG component.

Let’s take a look at how they can be achieved.

Approach #1

Single SVG Icon component demo

Link to CodeSandbox

TL;DR: We create a single SVG component and pass a name prop to it. The component resolves the viewBox and path values associated with the icon and returns the SVG element.

Lets start with adding the name prop to our SVG component and resolve the path for that name prop.

const getPath = (name, props) => {
switch(name) {
case 'icon-1':
return <path {...props} d="icon-1-path" />;
case 'icon-2':
return <path {...props} d="icon-2-path" />;
default:
return <path />;
}
}
const SVG = ({
name = '',
style = {},
fill = '#000',
width = '100%',
className = '',
height = '100%',
viewBox = '0 0 32 32',
}) =>
<svg
width={width}
style={style}
height={height}
viewBox={viewBox}
className={className}
xmlns="http://www.w3.org/2000/svg"
xmlnsXlink="http://www.w3.org/1999/xlink"
>
{getPath(name, { fill })}
</svg>;

Works great. But we haven’t considered that each SVG icons can have their own viewBox values. So we also need to resolve the viewBox based on the name prop.

const getViewBox = name => {
switch(name) {
case 'icon-1':
return 'icon-1-view-box'; // Eg. 0 0 32 32
default:
return '';
}
}
<svg
width={width}
style={style}
height={height}
className={className}
viewBox={getViewBox(name)}
xmlns="http://www.w3.org/2000/svg"
xmlnsXlink="http://www.w3.org/1999/xlink"
>
{getPath(name, { fill })}
</svg>;

And that’s it. We can add more paths and viewBoxes to this component and use it by adding the name prop for the icon we need.

<SVG fill="#49c" width={100} name="icon-1" />

Approach #2

SVG Component using multiple SVG files

Link to CodeSandbox

TL;DR: We create separate files for each SVG icon and create an index file that returns the SVG component based on the name prop.

We create separate components for each SVG icon we want.

./icons
--/Phone.js
--/Trash.js
--/Messages.js
--/Envelope.js
--/Wifi.js

Each component is independent of one another and can be used on their own.

import Phone from './icons/Phone';<Phone width={100} />

We then create an index file that returns the component itself based on the name prop.

./icons
--/Phone.js
--/Trash.js
--/Messages.js
--/Envelope.js
--/Wifi.js
--/...
--/index.js

The index file would look something like this.

import React from 'react';import Phone from './Phone';
import Messages from './Messages';
const Icon = props => {
switch(props.name) {
case "phone":
return <Phone {...props} />;
case "messages":
return <Messages {...props} />;
default:
return <div />;
}
}
export default Icon;

So anytime we need to add new icons into the mix, we create new components and include them in the index file. We use this component by importing a single Icon component and sending the name prop into it.

import Icon from './icons';<Icon fill="#49c" width={100} name="phone" />

And that’s it. I’ve detailed a few ways to create React components to manipulate SVG images. Of course these aren’t the only ways, or the even best ways to deal with SVGs in React applications. Just like anything in the world of Javascript, there are always other options at our disposal.

Update

As part of features added to the new version of create-react-app (v2), we can now import SVGs as React components.

import { ReactComponent as Icon} from './icon.svg';<Icon />

This method of importing SVGs into the react app should be sufficient for a large set of simple SVG icons. However, if you need lots of customization when using SVGs, I would recommend building a React component using SVG paths as detailed above.

Alternate options

Webpack SVG Loader — A webpack loader to import SVG files as components.

React Inline SVG — A react component that takes SVG file paths as prop to render them on the document.

--

--