DEV Community

Nishan Bajracharya
Nishan Bajracharya

Posted on • Originally published at blog.lftechnology.com on

Using SVG Icons Components in React

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.

Photo by NordWood Themes on Unsplash

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>;
Enter fullscreen mode Exit fullscreen mode

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>;
Enter fullscreen mode Exit fullscreen mode

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

Link to CodeSandbox

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

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>;
Enter fullscreen mode Exit fullscreen mode

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>;
Enter fullscreen mode Exit fullscreen mode

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" />
Enter fullscreen mode Exit fullscreen mode

Approach #2

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
Enter fullscreen mode Exit fullscreen mode

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

import Phone from './icons/Phone';

<Phone width={100} />
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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;
Enter fullscreen mode Exit fullscreen mode

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" />
Enter fullscreen mode Exit fullscreen mode

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.

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.

This article was last published in Medium. Using SVG Icons Components in React


Top comments (0)

Some comments may only be visible to logged-in visitors. Sign in to view all comments.