SVG: The Hard Parts — II
In our previous post we discussed why SVGs are an important part of our web app’s anatomy and what are the advantages and disadvantages of the current methods of using SVGs on any web app.
Website Performance is an integral part of the product strategy and is positively correlated to the conversation rate. Hence it is imperative to focus on optimising every single facet of the web app. In this blog we will discuss how did we optimally integrate SVGs into our React Application.
How did we bundle the SVG’s?
While planning the architecture for a scalable solution to the problems we have already discussed in the previous blog, the primary concern was to be able to bundle the SVG’s so that only the SVGs that were required on a page would be loaded. Having tried this with Svg-Sprite Loader and analyzing the pitfalls, we decided to exploit the dynamic ‘import()’ feature of webpack — which spits out the requested module and its children in separate chunks. To achieve this, we created a Dictionary containing the names of SVGs and their path values, and then dynamically imported the same.
We handled SVG Dictionary Creation in two ways:
i. Route-Level Dictionary: For every parent level component we created SVG records that were supposed to be loaded on the page, which could then be fetched during navigation or direct landing, after a specified event trigger eg: load, document interactive.
ii. Independent-Component Bundling: For independent components that were displayed after user interaction eg: Sidebars, Modals, etc. SVGs were loaded when the component was fetched into the viewport.
This process, though manual, allowed greater control over which SVGs were being fetched.
Could React help make SVG’s reusable?
Having bundled the required SVG’s in a separate chunk, the next milestone was to ensure that the reusability of SVGs is retained throughout the project. Since React Architecture is known for Reusable UI Components, we needed to leverage this to our benefit by somehow converting SVG’s into React Components.Could this be possible?
Let’s take a Look!
First step, we did was, parse the Dictionary, optimize all the SVGs using webpack loader, ‘svgo-loader’ which removed namespace attributes (xml,xmlns, etc).
Next, we wrote a custom webpack loader — “ Svg-react-loader”
In webpack, the loader is basically a piece of code that acts as a processing pipeline for a certain type of file extension and decorates it. Webpack has made the overall process of creating a custom loader pretty effortless. A very well documented guide from webpack for setting up a custom loader helps you get going swiftly.
Our custom Svg-react-loader basically accepted an XML format SVG and transformed it into a React component, decorated with custom props!! This means we could pass differentiating parameters like background-color, height, width etc. as props to the same SVG React Component and transform it for different use cases in multiple places. The below piece of code shows the loader transpiling the raw SVGs into react components.
Raw SVG to React Component
Finally, this React SVG Component was funneled to the babel-loader to spit out the Javascript code.
How did we integrate SVGs holistically in a React Component?
In the race to optimize any web app, the loading of some assets has to be prioritized above others. While SVGs are useful for iconography and making the web app pop, they should be fetched after all the main assets have been loaded.We had to explore a solution which would allow fetching of the SVG Chunks after some event has occurred.
Event-Driven Fetching of SVG Chunks
We created a Service that would dynamically fetch a bundle of SVGs when a browser event was triggered and then re-render the component with SVGs in it.
Introducing the SVG-Service
Implementation of SVG Service Handler:-
Important parameters to the service are noted below:
i. dynamicFileFetch: Path to the Dictionary which contained the mapped SVG object.
ii. eventToRegister: Event upon which the subscribing components SVG-Chunk must be fetched for example ‘load’, ‘DOMContentLoaded’ etc.
iii. eventTarget: Target element to register the event upon. The default element is ‘document’, but we can use any DOM element.
iv. rerenderParent: A re-rendering method to be used to re-render the subscribing component once its SVG-Chunk had been fetched. We could use forceUpdate(). However pure-components or components using shouldComponentUpdate() require the use of setState() to set a flag in the state and hence re-render the component (as props are not updated in this process)
let rerenderParent=this.setState({svgAavailable:true});
Consequently, this would follow, if a subscribing component has pure-children or children using shouldComponentUpdate() and hence such children would require an additional prop being passed which would be the svgAvailable flag set in the subscribing component’s state.
Now we just needed a getter to access SVGs
“getSVG( <string>svgName )”
This getter method was called with the name of the SVG we wanted to extract outta the fetched chunk, that if present in the chunk was returned as a React Component.
This getter method’s reference could be passed down into the children as a “prop” so that they could extract their own SVGs outta the chunk. By default, until the SVG-Chunk hadn’t arrived, the getter method used to return a null component and hence nothing showed. This behavior, however, could be changed. Now when the registered event fires for the service-subscribed component, its SVG-Chunk was fetched, and the subscribed component was re-rendered allowing the getter method to extract the SVGs outta the chunk.
When the component was unmounted from the DOM, it was the time to unsubscribe the SVG-Service, which was achieved with the help of another method exposed in the SVG service i.e.
“removeChangeListener(<string> eventName)”
This method was suitably called inside the componentWillUnmount() lifecycle method of the subscribed React component with the eventName argument i.e. the event which was registered during initialization.
Usage of the SVG Service
Conclusion
As this service was rolled throughout our app, it completely transformed the way SVGs were fetched and displayed. Some of the striking observations are penned below
1.Improved important browser metrics like Page Load Time[2s(slow 3G), 1.2s(fast 3G) and 600ms(4G)] and Time To Interaction.
2.A consequence of converting SVGs to React Components (into .js files ) was that these chunks could be compressed to give even smaller file sizes and hence lower fetch times.
3.Reduced workload for our graphics team which now had to make fewer SVGs as many SVGs were reusable.
A lot of hard work and thinking and experimenting has gone into the development of this approach. This blog was possible due to the contribution of Zubair Bashir, Sujay Raj, Prashant Singh and the rest of the MWEB Team at FabHotels.