Using SVG is currently the best way to create icon libraries for apps. Icons
built with SVG are scalable and adjustable, but also discrete, which allows
them to be incrementally loaded and updated. In contrast, icons built as fonts
cannot be incrementally loaded or updated. This alone makes SVG icons the
better choice for high-performance apps that rely on code-splitting and
incremental deploys.

This post describes how to make a package of React components from a library of
SVG icons. Although I’m focusing on React, making any other type of package is
also possible. At Twitter I used the approach described here to publish the
company’s SVG icon library in several different formats: optimized SVGs, plain
JavaScript modules, React DOM components, and React Native components.

Using the icons

The end result is a JavaScript package that can be installed and used like
any other JavaScript package.

yarnpkg add @acme/react-icons

Each icon is available as an individually exported React component.

import IconCamera from '@acme/react-icons/camera';

This allows your module bundler to package only the icons that are needed, and
icons can be efficiently split across chunks when using code-splitting. This is
a significant advantage over icon libraries that require fonts and bundle all
icons into a single component.

// entire icon library is bundled with your app
import Icon from '@acme/react-icons';
const IconCamera = <Icon name='camera' />;

Each icon is straightforward to customize (e.g., color and dimensions) on a
per-use basis.

import IconCamera from '@twitter/react-icons/camera';
const Icon = ( <IconCamera style={{ color: 'white', height: '2em' }} />
);

Although the icons render to SVG, this is an implementation detail that isn’t
exposed to users of the components.

Creating components

Each React component renders an inline SVG, using path and dimensions data
extracted from the SVG source files. A helper function called
createIconComponent means that only a few lines of boilerplate are needed to
create a component from SVG data.

import createIconComponent from './utils/createIconComponent';
import React from 'react';
const IconCamera = createIconComponent({ content: <g><path d='...'></g>, height: 24, width: 24
});
IconCamera.displayName = 'IconCamera';
export default IconCamera;

This is an example of what the createIconComponent function looks like when
building components for a web app like Twitter
Lite
, which is built with React Native for
Web
.

// createIconComponent.js
import { createElement, StyleSheet } from 'react-native-web';
import React from 'react'; const createIconComponent = ({ content, height, width }) => (initialProps) => { const props = { ...initialProps, style: StyleSheet.compose(styles.root, initialProps.style), viewBox: `0 0 ${width} ${height}` }; return createElement('svg', props, content); }; const styles = StyleSheet.create({ root: { display: 'inline-block', fill: 'currentcolor', height: '1.25em', maxWidth: '100%', position: 'relative', userSelect: 'none', textAlignVertical: 'text-bottom' }
});

Setting the fill style to currentcolor allows you to control the color of
the SVG using the color style property instead.

All that’s left is to use scripts to process the SVGs and generate each React
component.

Creating icon packages

A complete example of one way to do this can be found in the
icon-builder-example
repository on GitHub.

The project structure of the example tool looks like this.

.
├── README.md
├── package.json
├── scripts/ ├── build.js ├── createReactPackage.js └── svgOptimize.js
└── src/ ├── alerts.svg ├── camera.svg ├── circle.svg └── ...

The build script
uses SVGO
to optimize the SVGs, extract SVG path data, and extract metadata. The example
packager for React
then uses templates to create a package.json and the React icon components shown earlier.

import createIconComponent from './utils/createIconComponent';
import React from 'react';
const ${componentName} = createIconComponent({ height: ${height}, width: ${width}, content: <g>${paths}</g>
});
${componentName}.displayName = '${componentName}';
export default ${componentName};

Additional packagers can be included to build other package types from the same
SVG source. When the underlying icon library changes, it only takes a couple of
commands to rebuild hundreds of icons and publish new versions of each package.

Leave a comment

This site uses Akismet to reduce spam. Learn how your comment data is processed.

Top
%d bloggers like this: