React SVGs

reactMay 19, 2017Dotby Rob

What follows is a quick rundown of how we’re currently handling SVGs at ShakaCode.

Our philosophy is to make a component for each SVG and (to the extent possible) leave styling up to CSS. In the interest of simplicity, we’ve decided not to care about using sprites or refs to improve performance, as doing so would greatly increase complexity without guarantee of any noticeable gain. For more info on this and SVGs in React generally, see this awesome article by Sarah Drasner over at CSS-tricks that served as the basis for our architecture.

Setup: Make an SvgTemplate Component

To aid in creating our SVG components, we first created a generic SVG utility component that serves as a sort of wrapper for each of our SVGs’ path code. It looks like this:

// @flow

import React from 'react';

type Props = {

  children?: React.Element<*>,

  title: string,

  viewBoxMinWidth?: number,

  viewBoxMinHeight?: number,

  viewBoxWidth: number,

  viewBoxHeight: number,

};

const SvgTemplate = (

  {

    children,

    title,

    viewBoxMinWidth = 0,

    viewBoxMinHeight = 0,

    viewBoxWidth,

    viewBoxHeight,

    ...otherProps

  }: Props,

) => (

  <svg

    viewBox={`${viewBoxMinWidth} ${viewBoxMinHeight} ${viewBoxWidth} ${viewBoxHeight}`}

    xmlns="http://www.w3.org/2000/svg"

    aria-labelledby={`${title}-title`}

    {...otherProps}

  >

    <title id={`${title}-title`}>{title}</title>

    {children}

  </svg>

);

export default SvgTemplate;

Optimizing the SVG

We always make sure to run our SVGs through the web interface for SVGOMG. It’s great for stripping unnecessary metadata and other cruft left behind by tools like Illustrator or Sketch, and can also do some optimizations to make the SVG code more efficient (that is, effect the same outcome with fewer characters). We run it with pretty much every optimization enabled, except we leave the viewBoxHeight and viewBoxWidth intact.

Creating the SVG Component

With the optimized code in-hand, we then create our component using the SvgTemplate component shown above to keep the code DRY. Here is an example IconCaret component where we provide some overridable defaults:

// @flow

import React from 'react';

import SvgIconTemplate from 'libs/templates/SvgIconTemplate';

type Props = {

  title?: string,

  viewBoxWidth?: number,

  viewBoxHeight?: number,

};

const IconCaret = (

  {

    title = 'Caret',

    viewBoxWidth = 12,

    viewBoxHeight = 7,

    ...otherProps

  }: Props,

) => (

  <SvgIconTemplate {...{ title, viewBoxWidth, viewBoxHeight, ...otherProps }}>

    <polygon stroke="none" fillRule="evenodd" points="1 0 6 0 11 0 12 1 6 7 0 1" />

  </SvgIconTemplate>

);

export default IconCaret;

Styling the SVG Component

Note that we made sure not to include a fill attribute in the above component; doing so is essentially an inline style, which takes precedence over any styles set in an external stylesheet. Also note that we do not define a height or width as, again, we want to be able to use CSS to configure this (using CSS to style SVGs helps to keep the content code separate from the style code). If you assign a className of .IconCaret to the SVG component, you would be able to style it as follows:

.IconCaret {

  width: 20px;

  height: 20px;

  fill: #000;

}

Sometimes you may need to apply styles directly to SVG element tags such as rect or path, it just depends on the SVG:

.IconCaret path {

  // ...

}

Gotcha: Namespace Collisions when Optimizing

We started noticing that on pages where we rendered many SVG components, some of the icons would start to look very strange—like they were warped or had paths missing.

Eventually, we realized the issue was due to namespace collisions with different components’ g tags’ ids. When SVG code through an optimizer like SVGOMG, it will minimize the id names to a, b, etc. Since it does this each time, multiple components had ids like a or b. Our workaround was to manually choose unique ids for each SVG component type.

Closing Remark

Could your team use some help with topics like this and others covered by ShakaCode's blog and open source? We specialize in optimizing Rails applications, especially those with advanced JavaScript frontends, like React. We can also help you optimize your CI processes with lower costs and faster, more reliable tests. Scraping web data and lowering infrastructure costs are two other areas of specialization. Feel free to reach out to ShakaCode's CEO, Justin Gordon, at justin@shakacode.com or schedule an appointment to discuss how ShakaCode can help your project!
Are you looking for a software development partner who can
develop modern, high-performance web apps and sites?
See what we've doneArrow right