Last active
June 23, 2021 21:36
-
-
Save Cornally/8a78f057be9d5992293f8960817ce417 to your computer and use it in GitHub Desktop.
ReactJS — Ultimate SVG Icon Workflow. The following will guide you through an SVG icon workflow I've refined for easing the use of icons in small to large React applications. The end result is a tidy component that works in your React 16.8+ application and Storybook 6.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// React icon component | |
import React, { useMemo } from 'react' | |
import PropTypes from 'prop-types' | |
import classNames from 'classnames' | |
import Actions from '~images/icons/actions.inline.svg' | |
import ArrowUp from '~images/icons/arrow-up.inline.svg' | |
import ArrowRight from '~images/icons/arrow-right.inline.svg' | |
import Beaker from '~images/icons/beaker.inline.svg' | |
import Comment from '~images/icons/comment.inline.svg' | |
import Delete from '~images/icons/delete.inline.svg' | |
import Heart from '~images/icons/heart.inline.svg' | |
import LoaderRings from '~images/icons/loader-rings.inline.svg' | |
import Padlock from '~images/icons/padlock.inline.svg' | |
import useStyles from './icon.styles' | |
const Icon = ({ className, name, rotate, size }) => { | |
const classes = useStyles() | |
const getClasses = useMemo(() => classNames( | |
[classes.icon], { | |
[className]: !!className | |
} | |
), [className, classes.icon]) | |
const getIconContents = name => { | |
switch (name) { | |
case 'actions': | |
return Actions | |
case 'arrow-right': | |
return ArrowRight | |
case 'arrow-up': | |
return ArrowUp | |
case 'beaker': | |
return Beaker | |
case 'comment': | |
return Comment | |
case 'delete': | |
return Delete | |
case 'heart': | |
return Heart | |
case 'loader-rings': | |
return LoaderRings | |
case 'padlock': | |
return Padlock | |
default: | |
return null | |
} | |
} | |
return ( | |
<div | |
dangerouslySetInnerHTML={{ __html: getIconContents(name) }} | |
className={getClasses} | |
style={size || rotate ? { | |
width: size, | |
height: size, | |
transform: `rotate(${rotate}deg)` | |
} : null} | |
/> | |
) | |
} | |
Icon.propTypes = { | |
/** Select icon to display */ | |
name: PropTypes.string.isRequired, | |
/** Pass a value to rotate the icon */ | |
rotate: PropTypes.number, | |
/** Set a size in px, em, etc. */ | |
size: PropTypes.oneOfType([PropTypes.number, PropTypes.string]) | |
} | |
Icon.defaultProps = { | |
rotate: 0, | |
size: '1em' | |
} | |
export default Icon |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Icon component styles | |
import { createUseStyles } from 'react-jss' | |
export default createUseStyles({ | |
icon: { | |
fill: 'currentColor', | |
width: '1em', | |
height: '1em', | |
display: 'flex', | |
justifyContent: 'center', | |
alignItems: 'center', | |
'& svg': { | |
width: '100%', | |
height: '100%' | |
} | |
} | |
}) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Customize CRA Configuration | |
const { | |
addWebpackAlias, | |
addWebpackModuleRule, | |
override | |
} = require('customize-cra'); | |
const path = require('path') | |
module.exports = override( | |
addWebpackModuleRule({ | |
test: /\.inline\.svg$/, | |
use: 'svg-inline-loader' | |
}), | |
addWebpackAlias({ | |
'~components': path.resolve(__dirname, 'src/components'), | |
'~constants': path.resolve(__dirname, 'src/common/constants/constants'), | |
'~images': path.resolve(__dirname, 'src/images'), | |
'~middleware': path.resolve(__dirname, 'src/common/middleware'), | |
'~mixins': path.resolve(__dirname, 'src/styles/mixins'), | |
'~styles': path.resolve(__dirname, 'src/styles/styles'), | |
// Redux aliases — note the unique prefix, '@' | |
'@': path.resolve(__dirname, 'src/data'), | |
}) | |
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Storybook 6.x Configuration | |
const path = require('path') | |
module.exports = { | |
stories: ['../src/**/*.stories.@(ts|js)'], | |
addons: [ | |
'@storybook/preset-create-react-app', | |
'@storybook/addon-actions', | |
'@storybook/addon-links', | |
], | |
webpackFinal: async (config, { configType }) => { | |
// By default, storybook 5.32/6+ throw all files without a respective loader at `file-loader` in a `oneOf` loop. | |
// Add the exclusion of inline SVGs (e.g. icon-name.inline.svg) to mimic webpack overridden CRA configuration. | |
config.module.rules = config.module.rules.map(rule => { | |
if (rule.oneOf) { | |
rule.oneOf = rule.oneOf.map(item => { | |
if (item.loader && item.loader.includes('file-loader')) { | |
item.exclude.push(/\.inline\.svg$/) | |
} | |
return item | |
}) | |
} | |
return rule | |
}) | |
config.module.rules.push({ | |
test: /\.inline\.svg$/, | |
use: [ | |
{ loader: require.resolve('svg-inline-loader') } | |
] | |
}); | |
config.resolve.alias = { | |
...config.resolve.alias, | |
'~components': path.resolve(__dirname, '../src/components'), | |
'~constants': path.resolve(__dirname, '../src/common/constants/constants'), | |
'~images': path.resolve(__dirname, '../src/images'), | |
'~middleware': path.resolve(__dirname, '../src/common/middleware'), | |
'~mixins': path.resolve(__dirname, '../src/styles/mixins'), | |
'~styles': path.resolve(__dirname, '../src/styles/styles'), | |
// Redux aliases — note the unique prefix, '@' | |
'@': path.resolve(__dirname, '../src/data') | |
} | |
return config | |
} | |
}; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment