Support SVG’s in JS
This tutorial was contributed by Athira
In this chapter, we are going to discuss how to use SVGs in JS
without causing an impact on performance.
What is SVG’s in JS?
Like HTML, SVGs are represented using the Document Object Model (DOM) and so can be manipulated with Javascript relatively easily.
Problem
There are SVG’s embedded in JS for icons / logos / etc. These cause an impact on performance because:
- The size of the JS increases with additions and entail cost over the network.
- Render blocking.
- Downloaded JS is parsed affecting FCP & FMP.
Solution
SVG’s should be made external via webpack / plugins to load them lazy via a network call or DOM reference. Thus we are using SVG sprite loader
(Webpack loader for creating SVG sprites).
Why SVG sprite loader?
SVG Sprites are rendered and injected in pages automatically, you just refer to images via <svg><use xlink:href="#id"></use></svg>
. Also it can render sprites on server or in browser manually.
Set up SVG sprite loader for your app
Note: Currently this feature is available in Malibu. Please follow the below steps if your app is cloned from Malibu before 25th June 2021.
There are few steps you need to follow to set up SVG sprite loader:
- The first thing you need to install the package
npm install svg-sprite-loader -D
. - Go to
webpack.config.js
and modify the webpack config to support svg-sprite-loader.
const webpackConfig = require("@quintype/build/config/webpack");
const path = require("path");
const SpriteLoaderPlugin = require("svg-sprite-loader/plugin");
const svgSprite = {
test: /\.svg$/,
loader: "svg-sprite-loader",
options: {
extract: true,
publicPath: "/",
symbolId: filePath => {
return path
.basename(filePath)
.replace(".svg", "")
.toLowerCase();
},
spriteFilename: "sprite.svg",
esModule: false
}
};
webpackConfig.module.rules.push(svgSprite);
webpackConfig.module.rules.find(rule => rule.loader === "file-loader").exclude = [/app\/assets\/icons\/[a-z-]+\.svg$/];
const svgPlugin = () =>
new SpriteLoaderPlugin({
plainSprite: true
});
webpackConfig.plugins.push(svgPlugin());
module.exports = {
...webpackConfig
};
Once you compile(build) the webpack, sprite.svg
will be generated and available in the public folder.
3 . Runtime configuration When you require an image, the loader transforms it to SVG
-
Add all the svg icons inside
/assets/icons/
folder. -
Go to
load-data.js
file and pass the webpack generated sprite.svg’s path name as a part of config.
...
import { assetFiles as getAssetFiles } from "@quintype/framework/server/asset-helper";
...
const svgSpritePath = Array.from(getAssetFiles()).find(asset => asset.includes("sprite"));
...
...
config: Object.assign(pick(config.asJson(), WHITELIST_CONFIG_KEYS), {
...
svgSpritePath
})
...
...
- Now create a
SvgIconHandler
component, for that create a new folder insideatom
. Example:malibu/app/isomorphic/components/atoms/svg-icon-hadler/index.js
import React from "react";
import { useSelector } from "react-redux";
import { get } from "lodash";
import { string, object } from "prop-types";
if (require.context) { // webpack's compiler that allows you to get all matching modules starting from some base directory
const req = require.context("../../../../assets/icons/", true, /\.svg$/); //dynamically importing all the svg icons.
req.keys().forEach(filename => req(filename));
}
export const SvgIconHandler = ({
type,
className = "",
iconStyle = {},
width = "16",
height = "16",
viewBox = "0 0 16 16"
}) => {
const svgSpritePath = useSelector(state => get(state, ["qt", "config", "svgSpritePath"], "")); // get the webpack generated sprite path.
return (
<svg className={className} style={iconStyle} width={width} height={height} viewBox={viewBox}>
<use href={`${svgSpritePath}#${String(type).toLowerCase()}`} />
</svg>
);
};
SvgIconHandler
component will accept type, className, iconStyle, width, height and viewBox
as props. The prop type
will be the required SVG icon name.
Example:
<SvgIconHandler type="user" width="24px" height="24px" viewBox="0 0 24 24" iconStyle= />
Note: In the above example, the user.svg
icon should be available in /assets/icons/
folder.