loading...

Creating my first React Package - React-unicons

tarunmangukiya profile image Tarun Mangukiya ・3 min read

Hi guys,

Since long I wanted to work on and create package in React. I am mostly working in VueJS, thus this was a bit new experience for me.

Recently, we launched Unicons - an icon library containing 1000+ vector icons in unique line style as open source. And since then I wished to have package for React. Last Sunday, I was kinda free & thought to give it a try! And here I am writing this article.

How to

There were few ideas in my mind related to how I want to make it available. Some of them contains:

  1. Easy to use for everyone.
  2. Component based
  3. One should be able to load only icons that they need, keeping bundle size as low as possible.

I wrote a script that converts the SVGs to React components. I used folder based approach like lodash to keep the bundle size smaller. Every component gets compiled into different React component file and gets imported to common index.js for those who want to use all the icons.

Here is a sample component of comment icon:

import React from 'react';
import PropTypes from 'prop-types';

const UilComment = (props) => {
  const { color, size, ...otherProps } = props
  return React.createElement('svg', {
    xmlns: 'http://www.w3.org/2000/svg',
    width: size,
    height: size,
    viewBox: '0 0 24 24',
    fill: color,
    ...otherProps
  }, React.createElement('path', {
    d: 'M12,2A10,10,0,0,0,2,12a9.89,9.89,0,0,0,2.26,6.33l-2,2a1,1,0,0,0-.21,1.09A1,1,0,0,0,3,22h9A10,10,0,0,0,12,2Zm0,18H5.41l.93-.93a1,1,0,0,0,0-1.41A8,8,0,1,1,12,20Z'
  }));
};

UilComment.propTypes = {
  color: PropTypes.string,
  size: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
};

UilComment.defaultProps = {
  color: 'currentColor',
  size: '24',
};

export default UilComment;

The build script that I wrote looks something like this:

const path = require('path')
const fs = require('fs-plus')
const cheerio = require('cheerio')
const upperCamelCase = require('uppercamelcase')

const iconsComponentPath = path.join(process.cwd(), 'icons')
const iconsIndexPath = path.join(process.cwd(), 'index.js')
const uniconsConfig = require('@iconscout/unicons/icons.json')

// Clear Directories
fs.removeSync(iconsComponentPath)
fs.mkdirSync(iconsComponentPath)

const indexJs = []

uniconsConfig.forEach(icon => {
  const baseName = `uil-${icon.name}`
  const location = path.join(iconsComponentPath, `${baseName}.js`)
  const name = upperCamelCase(baseName)
  const svgFile = fs.readFileSync(path.resolve('node_modules/@iconscout/unicons', icon.svg), 'utf-8')

  let data = svgFile.replace(/<svg[^>]+>/gi, '').replace(/<\/svg>/gi, '')
  // Get Path Content from SVG
  const $ = cheerio.load(data, {
    xmlMode: true
  })
  const svgPath = $('path').attr('d')

  const template = `import React from 'react';
import PropTypes from 'prop-types';
const ${name} = (props) => {
  const { color, size, ...otherProps } = props
  return React.createElement('svg', {
    xmlns: 'http://www.w3.org/2000/svg',
    width: size,
    height: size,
    viewBox: '0 0 24 24',
    fill: color,
    ...otherProps
  }, React.createElement('path', {
    d: '${svgPath}'
  }));
};
${name}.propTypes = {
  color: PropTypes.string,
  size: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
};
${name}.defaultProps = {
  color: 'currentColor',
  size: '24',
};
export default ${name};`

  fs.writeFileSync(location, template, 'utf-8')

  // Add it to index.js
  indexJs.push(`export { default as ${name} } from './icons/${baseName}'`)
})

fs.writeFileSync(iconsIndexPath, indexJs.join('\n'), 'utf-8')

console.log(`Generated ${uniconsConfig.length} icon components.`)

Earlier I tried to use webpack and it didn't work out properly as it compiled them into single index.js file.

Publishing it as npm package

I learned how to publish it as a npm package so that everyone can use it via simple npm install. I've published it at https://www.npmjs.com/package/@iconscout/react-unicons

Use

You can use these icon by simple installing
npm i -s @iconscout/react-unicons

and then import individual icons in your component.

import React from 'react';
import UilReact from '@iconscout/react-unicons/icons/uil-react'

const App = () => {
  return <UilReact size="140" color="#61DAFB" />
};

export default App;

I have also added props to customise color and size.

I would love to have suggestions from DEV.to community on how I can improve it. :)

Discussion

pic
Editor guide