- How to Use SVG Icons as React Components?
- Original article by ROBIN WIERUCH
- The Nuggets translation Project
- Permanent link to this article: github.com/xitu/gold-m…
- Translator: TiaossuP
- Proofreader: vitoxli, ZavierTang
How to use SVG ICONS as React components?
I’ve been confused about using SVG in the React app. I’ve seen a lot of schemes for using SVG in React on the Internet, but when it comes to practical use, almost none have been successful. Now I’m going to introduce a very easy way to solve this problem.
Tip: The ICONS in this article are fromFlaticon. Don’t forget to thank the author/platform if you use the site icon!
You can create a special folder in your React project to store.svg icon files. This way, you can manually/automatically generate React components. In the next two sections, I’ll show you how to manually create the icon component using the command line and NPM commands, and how to automatically create the React component using webpack. Both approaches use a tool called SVGR (which is also widely used by tools like create-React-app).
Create the React SVG icon component from the command line
In this section, we start by manually creating SVG ICONS. If you need the starter project from this article, poke the WebPack + Babel + React project and refer to its installation instructions.
Next, create an /assets directory in your SRC/directory and put all your.svg ICONS in it. We don’t want to confuse the resource files with the source code, as we will then generate JavaScript files based on these resource files. These manually generated JavaScript files — the React icon component — will be mixed in with your other source code.
assets/
-- twitter.svg
-- facebook.svg
-- github.svg
src/
-- index.js
-- App.js
Copy the code
Now, create a SRC /Icons/ folder for your React icon component:
assets/
-- twitter.svg
-- facebook.svg
-- github.svg
src/
-- index.js
-- App.js
-- Icons/
Copy the code
We want the resulting React icon component to be used in SRC/app.js:
import React from 'react';
import TwitterIcon from './Icons/Twitter.js';
import FacebookIcon from './Icons/Facebook.js';
import GithubIcon from './Icons/Github.js';
const App = (a)= > (
<div>
<ul>
<li>
<TwitterIcon width="40px" height="40px" />
<a href="https://twitter.com/rwieruch">Twitter</a>
</li>
<li>
<FacebookIcon width="40px" height="40px" />
<a href="https://www.facebook.com/rwieruch/">Facebook</a>
</li>
<li>
<GithubIcon width="40px" height="40px" />
<a href="https://github.com/rwieruch">Github</a>
</li>
</ul>
</div>
);
export default App;
Copy the code
However, nothing is happening at the moment because the SRC /Icons directory is now empty with no icon components in it. Next, we will generate the React icon component by adding a new NPM script in package.json, using the assets folder as the source folder and SRC /Icons as the target folder.
{..."main": "index.js"."scripts": {
"svgr": "svgr -d src/Icons/ assets/"."start": "webpack-dev-server --config ./webpack.config.js --mode development"
},
"keywords": [],... }Copy the code
Finally, install the SVGR CLI package using the command line.
npm install @svgr/cli --save-dev
Copy the code
Now that everything is ready, you can execute your new NPM command by typing NPM run SVGR on the command line. You can see the JavaScript file generated from the SVG file in the command line output. After the command is executed, you can launch the application and see the SVG icon rendered as the React component on the page. You can see all the React icon components generated in the SRC /Icons directory. These components can also take props as an argument, which means you can define the width and height of the icon.
This is the whole process of generating a React component from SVG. Every time you add a new SVG file or modify an existing SVG file, you need to run the NPM run SVGR command again.
Use WebPack to generate the React SVG icon component
Manually executing commands every time you update an SVG file is not an optimal solution. You can integrate it into a WebPack configuration. Now you can clear your SRC /Icons folder, move the SVG files from assets/ to SRC /Icons and delete assets/. Your directory structure will now look like this:
src/
-- index.js
-- App.js
-- Icons/
---- twitter.svg
---- facebook.svg
---- github.svg
Copy the code
You can now reference SVG files directly in your App components.
import React from 'react';
import TwitterIcon from './Icons/Twitter.svg';
import FacebookIcon from './Icons/Facebook.svg';
import GithubIcon from './Icons/Github.svg';
const App = (a)= > (
<div>
<ul>
<li>
<TwitterIcon width="40px" height="40px" />
<a href="https://twitter.com/rwieruch">Twitter</a>
</li>
<li>
<FacebookIcon width="40px" height="40px" />
<a href="https://www.facebook.com/rwieruch/">Facebook</a>
</li>
<li>
<GithubIcon width="40px" height="40px" />
<a href="https://github.com/rwieruch">Github</a>
</li>
</ul>
</div>
);
export default App;
Copy the code
Now that launching the application will fail, importing SVG files is obviously not that simple. However, we can have WebPack automatically introduce the correct SVG file every time the application is launched. Modify your webpack.config.js file by referring to the code below.
const webpack = require('webpack');
module.exports = {
entry: './src/index.js'.module: {
rules: [{test: /\.(js|jsx)$/.exclude: /node_modules/.use: ['babel-loader'],}, {test: /\.svg$/.use: ['@svgr/webpack'],},],},... };Copy the code
Then, install the necessary Webpack packages for SVGR
npm install @svgr/webpack --save-dev
Copy the code
Once the application starts, Webpack will work automatically and you won’t have to worry about SVG files anymore. You can place your SVG file anywhere in the SRC/directory and reference it by any React component. Now we no longer need SVGR commands in package.json files.
Another alternative: react-SVG-loader
If you use WebPack, you can use some simpler SVG loaders instead of SVGR. For example, you can use react-SVG-loader instead of SVGR in your Webpack configuration:
const webpack = require('webpack');
module.exports = {
entry: './src/index.js'.module: {
rules: [{test: /\.(js|jsx)$/.exclude: /node_modules/.use: ['babel-loader'],}, {loader: 'react-svg-loader'.options: {
jsx: true // true outputs JSX tags}}],},... };Copy the code
Of course you need to install it first:
npm install react-svg-loader --save-dev
Copy the code
Then, you can still import and use your SVG files in the same way as SVGR. This is a lightweight alternative to SVGR.
Use SVGR templates to do some advanced applications
During my most recent development of React, I encountered some problematic SVG ICONS that had incomplete viewBox properties. Since this property is required to provide size for SVG ICONS, I had to find a way to introduce it as long as it wasn’t in the icon. I could have fixed the problem by iterating through every SVG icon, but dealing with 500 + ICONS is not an easy task. I’ll show you how to solve this problem with the SVGR template functionality.
The default SVGR template in the webpack.config.js file looks like this:
. module.exports = {entry: './src/index.js'.module: {
rules: [{test: /\.(js|jsx)$/.exclude: /node_modules/.use: ['babel-loader'],}, {test: /\.svg$/.use: [{loader: '@svgr/webpack'.options: {
template: (
{ template },
opts,
{ imports, componentName, props, jsx, exports }
) => template.ast`
${imports}
const ${componentName} = (${props}) => {
return ${jsx};
};
export default ${componentName};
`,},},],},},... };Copy the code
Using this template, you can change the code generated by SVG files. Suppose we want to fill all ICONS with blue. You just need to extend the props object with the fill attribute:
. module.exports = {entry: './src/index.js'.module: {
rules: [{test: /\.(js|jsx)$/.exclude: /node_modules/.use: ['babel-loader'],}, {test: /\.svg$/.use: [{loader: '@svgr/webpack'.options: {
template: (
{ template },
opts,
{ imports, componentName, props, jsx, exports }
) => template.ast`
${imports}
const ${componentName} = (${props}) => { props = { ... props, fill: 'blue' }; return${jsx};
};
export default ${componentName};
`,},},],},},... };Copy the code
This gives all ICONS a fill: Blue attribute. SVGR itself already provides a similar simple use case. Just look at their documentation to see how to add/replace/remove attributes to SVG.
Customize using SVGRviewBox
attribute
In our case, we want to add the viewBox attribute to every SVG icon that does not have one. First, we remove the viewBox property from a normal SVG, and now it will definitely not display properly. After confirming that the problem had occurred, we tried to fix it using the SVGR template described above and an additional React Hook:
import React from 'react';
const useWithViewbox = ref= > {
React.useLayoutEffect((a)= > {
if( ref.current ! = =null &&
// When there is no viewBox attribute! ref.current.getAttribute('viewBox') &&
// When jsDOM is bug-free
// https://github.com/jsdom/jsdom/issues/1423
ref.current.getBBox &&
// When it has been rendered
// https://stackoverflow.com/questions/45184101/error-ns-error-failure-in-firefox-while-use-getbbox
ref.current.getBBox().width &&
ref.current.getBBox().height
) {
const box = ref.current.getBBox();
ref.current.setAttribute(
'viewBox',
[box.x, box.y, box.width, box.height].join(' ')); }}); };export default useWithViewbox;
Copy the code
React Hook requires a reference (ref) to this SVG component to set its viewBox property. The viewBox properties will now be evaluated based on the rendered icon. If the icon has not been rendered, or the viewBox property already exists, we do nothing.
This hook should be placed not far from the SRC /Icons directory.
src/
-- index.js
-- App.js
-- useWithViewbox.js
-- Icons/
---- twitter.svg
---- facebook.svg
---- github.svg
Copy the code
Now we can add hooks to the SVG template in the webpack.config.js file:
. module.exports = {entry: './src/index.js'.module: {
rules: [{...test: /\.svg$/.use: [{loader: '@svgr/webpack'.options: {
template: (
{ template },
opts,
{ imports, componentName, props, jsx, exports }
) => template.ast`
${imports}import useWithViewbox from '.. /useWithViewbox'; const${componentName} = (${props}) => { const ref = React.createRef(); useWithViewbox(ref); props = { ... props, ref }; return${jsx};
};
export default ${componentName};
`,},},],},},... };Copy the code
With this capability, the template capability of SVGR adds custom hooks to each generated icon component. This hook is executed only in icon components that have no viewBox property. Run the application again at this point, and you’ll see that all icon components are displayed correctly — even though you may have removed the viewBox attribute from one of the components.
Finally, I hope this article has taught you how to introduce SVG ICONS in React using SVGR either on the command line/NPM command or in webpack. The final React app implemented using WebPack can be found in this GitHub repo. If you come across any bugs, let me know in the comments. I’d love to hear about something similar to “missing viewBox properties.” Let me know in the comments.
If you find any mistakes in your translation or other areas that need to be improved, you are welcome to the Nuggets Translation Program to revise and PR your translation, and you can also get the corresponding reward points. The permanent link to this article at the beginning of this article is the MarkDown link to this article on GitHub.
The Nuggets Translation Project is a community that translates quality Internet technical articles from English sharing articles on nuggets. The content covers Android, iOS, front-end, back-end, blockchain, products, design, artificial intelligence and other fields. If you want to see more high-quality translation, please continue to pay attention to the Translation plan of Digging Gold, the official Weibo, Zhihu column.