Build an SVG icon library in React
Working with design assets like svgs doesn't need to be hard. SVGR can help us transform svgs into ready to use React components to easily integrate with our projects.
SVGR transforms SVGs into React Components
Let's expolore SVGR by creating an SVG icon library and bundle it using Rollup to get it ready to be published on NPM.
Initialize the project
Make a project directory and cd into it.
mkdir tyger-avatar && cd tyger-avatar
Initialize NPM in the root folder of our project.
npm init -y
Create an svg assets folder and add your assets. The name of the svg will be later used to generate the React Component name.
├── assets │ ├── sampleOne.svg │ └── sampleTwo.svg └── package.json
SVGR Installation
Let's transform our svg files into reusable React Components using SVGR. Our svgs will be wrapped into a React component with customized props.
First, install SVGR CLI as a dev dependency.
npm install @svgr/cli --save-dev
Next, we need to add the SVGR script to package.json
. There are several customization options that can help us optimize our generated React component, including some advanced options, like creating a custom template.
For this project, we are using the following options:
--icon
: ViewBox is preserved and SVG inherits text size.--title-prop
: Add title tag via title property.--typescript
: Generates .tsx files.-d
Defines the directories. Outputsrc/TrAvatars
and inputassets
.
"scripts": { "svgr": "svgr --icon --title-prop --no-dimensions --typescript -d src/TrAvatars assets", },
Checkout more customization options.
SVGR Template (optional)
The SVGR template allow us to optionally customize the final generated component. The custom template exports a function called by the babel plugin (internally by SVGR). The template must return a Babel AST. This template is for advanced customizations. In most cases we won't need it.
Let's create a custom template file to customize the name of our exported component. Essentially, we're using the default template and removing the prefix "Svg" name from the generated component. view svgr template.
// svgr-template.js function defaultTemplate( { template }, opts, { imports, interfaces, componentName, props, jsx } ) { const tygerIcon = `${componentName.name.replace('Svg', '')}`; const plugins = ['jsx']; if (opts.typescript) { plugins.push('typescript'); } const typeScriptTpl = template.smart({ plugins }); return typeScriptTpl.ast`${imports} ${interfaces} function ${tygerIcon}(${props}) { return ${jsx}; } export default ${tygerIcon} `; } module.exports = defaultTemplate;
We need to let SVGR know about our file template. In the root of our directory create an .svgrrc.js
file.
// .svgrrc.js module.exports = { template: require('./svgr-template'), };
SVGR Transformation
We can convert our svgs to react components by running:
npm run svgr
This script will generate our React components and an index.tsx
entry point with all of the individual components. We will be using this file when we bundle our icon library.
├── assets │ ├── SampleOne.svg │ └── SampleTwo.svg ├── src | |-- TrAvatars | | |-- SvgSampleOne.tsx | | |-- SvgSampleTwo.tsx | | └── index.tsx └── package.json
Install React and TS
Now, install React and TypeScript as dev dependency.
npm i --save-dev react @types/react typescript
In package.json
add this snippet to use React as a peer dependency.
"peerDependencies": { "react": "^16.8.0" }
Lastly, let's create a tsconfig.json
in the root directory to specify the root files and the compiler options for the project.
{ "compilerOptions": { "target": "es5", "outDir": "dist", "lib": ["es6", "dom", "es2016", "es2017"], "declaration": true, "declarationDir": "dist", "allowJs": true, "skipLibCheck": true, "esModuleInterop": true, "allowSyntheticDefaultImports": true, "strict": true, "forceConsistentCasingInFileNames": true, "module": "esnext", "moduleResolution": "node", "resolveJsonModule": true, "isolatedModules": true, "noEmit": true, "jsx": "react" }, "include": ["src"], "exclude": ["node_modules", "dist"] }
Bundle with Rollup
Rollup is a module bundler for JavaScript. It's easy to configure and the perfect fit for our icon library. Install Rollup and the plugins that we need.
npm i --save-dev rollup rollup-plugin-typescript2 @rollup/plugin-node-resolve rollup-plugin-peer-deps-external
Rollup config
// rollup.config.js import peerDepsExternal from 'rollup-plugin-peer-deps-external'; import resolve from '@rollup/plugin-node-resolve'; import typescript from 'rollup-plugin-typescript2'; const packageJson = require('./package.json'); export default { // entry points input: './src/index.tsx', // output files output: [ { file: packageJson.module, format: 'esm', // ES Modules sourcemap: true, }, ], // Plugins array plugins: [ peerDepsExternal(), // prevents bundling peerDependencies resolve(), // resolves package entrypoints typescript({ useTsconfigDeclarationDir: true }), // typescript ], };
Now, add the entries to our project, in package.json
.
/* package.json */ "main": "dist/index.d.js", "module": "dist/index.esm.js", "types": "dist/index.d.ts",
Last, let's streamline the build process in one step, so that when we run npm run build
.
- Our
rc/TrAvatars
anddist
will have the most updated assets (delete/generate). - The
svgr
script will run and create our React components with our options. - And finally the rollup bundler will compile our library.
In package.json
update our scripts.
/* package.json */ "scripts": { "clean": "rm -rf src/TrAvatars && rm -rf dist", "svgr": "svgr --icon --title-prop --no-dimensions --typescript -d src/TrAvatars assets", "build": "npm run clean && npm run svgr && rollup -c", },
Consume our package
Now, we can publish our React SVG Icon Library on NPM and consume it in multiple projects. Also, we can add Storybook to have a visual reference to our library.
Hey, I'm Ignacio Villamar
Senior Frontend Engineer, living in the NYC metro area.
Follow @ivstudio