React, SVG Images and the webpack Loader to Make Them Play Nice

First published Jun 8, 2018 in ITNEXT

Laptop screen filled with code sitting on a couch

Photo by Goran Ivos on Unsplash

Introduction

This isn’t a new problem - the dilemma of getting SVGs to correctly render with React. But it is a problem that I hadn’t had to deal with and overcome until recently, and I’d like to share my experiences in the hope that it saves someone else a few hours of searching Github, StackOverflow and npm.

At my current company, I’ve worked for two separate development teams, both of whom use JavaScript microservices for their UIs: one uses AngularJS, the other JavaScript MVC. All the backend services are Java Spring Boot applications.

Recently, the news broke that Google was going to end long term support for AngularJS v1.5 - the version we were running on.

It quickly became clear we had three options:

  1. Upgrade the system to AngularJS 1.7 (which would continue to have long term support for 3 more years),
  2. Upgrade our application to Angular 2+,
  3. Or migrate to a competitor JavaScript framework: ReactJS.

One of my colleagues and I were tasked with figuring out which was the best direction to take. This exploration was a process which deserves its own blog post, but suffice it to say, I was most in favor of venturing into React (I liked the syntax better for one). After we’d looked at our options and tinkered, we decided to see if we could make a small proof-of-concept (POC) in React work with our existing app (because we'd need the two UIs to route back and forth to each other until the migration was complete).

About 4 hours after starting our React POC, we had a working, albeit not very pretty, app. We added Jest and Enzyme unit tests, Cypress end-to-end tests and more. But for almost two weeks one thing eluded us: getting an SVG graphic to render correctly in the DOM.

Today, I'll show you how I got SVGs working with a React application.

Why SVGs? What’s the Benefit? Why Not Just Make It Into a PNG?

Great question. The benefits of using SVGs are many, and in case you don’t know, SVG stands for Scalable Vector Graphics. While the textbook definition sounds dense:

“SVG is an XML-based vector image format for two-dimensional graphics with support for interactivity and animation” — Wikipedia, Scalable Vector Graphics

They basically take the place of traditional images in other formats like JPEG, PNG, TIFF or GIF.

Here are a few reasons why SVGs are superior to the other image formats:

  • Crystal clear resolution no matter the screen size, zoom level or image size — since SVGs are built on vector paths, shapes and fills, they can scale to any size with no loss of clarity. PNGs, on the other hand, are based on pixels, which won’t look sharp and clear at all times (especially on retina display devices).
  • Load speed is much faster — if you’re using high res images on your site to cater to those retina displays, the PNG files can be pretty sizable, and larger files render more slowly. SVGs are just code, which means smaller file sizes (unless you’re loading something very detailed with a million vector paths).
  • Animation is possible — unlike PNG files which are static: what you see is what you get - SVGs can be animated and styled with CSS. Animations, different colors, changes in state (like mouse hover or click) - all these things can be done to SVGs.
  • Accessibility and SEO — Google indexes SVGs, which means better SEO scores for your site and better accessibility for people with vision impairments visiting your site.

Right. So SVGs It Is, Now How to Use Them In React

For my project, I was attempting to recreate one of the simpler screens of the existing AngularJS application in React. This screen contained four big, clickable cards that would take users to other various screens, and each card had a nice SVG file associated with words.

So I copied the SVG files from the original project into the React app, imported the files into the component that would render them, and told the DOM to render them:

<Image source={require("../img/key.svg")} />

In the JSX, I added the CSS styling, waited for webpack to do its magical parsing and... nothing. Well, not quite nothing, errors in the console about unrecognized file types, but certainly no SVG key-shaped icon displayed in the UI.

And thus began the search for the library I needed to render SVGs correctly in React. This was a days long process. If you’re unaware there’s approximately 7,845,923 npm modules that all claim to make React play nicely with SVGs. Some of them probably work, the first 10 or so I tried, did not.

Then… SVG Inline Loader

Then I found it. An npm module aptly named svg-inline-loader, it had decent documentation, it had 3,200 weekly downloads, it had 325 stars on Github, it was developed and maintained by the core team that built webpack - it seemed promising.

So I gave it a shot and saved it to my devDependencies in the package.json file.

npm install svg-inline-loader react-inlinesvg --save-dev

Then added the configuration object to the webpack.config.js file:

{
    test: /\.svg$/,
    loader: "svg-inline-loader"
}

And I was good to go. One simple update to webpack, a small adjustment to the JSX syntax and...

import React, { Component } from 'react';
import KeyImage from '../img/key.svg'; 
import SVG from 'react-inlinesvg';
class Card extends Component(){
return(
    <div>
      < /* other UI stuff here */ > 
      <SVG src={KeyImage} />
    </div>  
  )
}

BOOM — SVG displayed!

I’d also like to add that this npm module has some pretty nifty extra features: it can remove extra tag info from SVGs (which happens a lot when they’re generated with Sketch or Adobe), and it can add classes or IDs to prevent SVG collisions. Nice.

And that was it. No CSS changes, no super weird syntaxes, nothing like that. Just svg-inline-loader, an extra module loader for webpack, and the problem was solved.

Thanks for reading, I hope it helps you like it helped me.

Further References & Resources

Want to be notified first when I publish new content? Subscribe to my newsletter.