We tend to use different external libraries for various purposes. The size of those libraries varies from small/medium/large. What happens when you want to use a large library only for a particular route?
It doesn’t make any sense to load that library along with the initial bundle or with the vendor. Such large libraries are needed only when a user navigates to that particular route.
This blog post will discuss how we can achieve this in a ReactJS application
For this blog post, let’s take the highcharts as the heavy library.
Without Lazy load
If you add basic highcharts
it will be ~150KB (gzipped). So without lazyload, you will be shipping this 150KB in the main bundle itself.
You can see this in action on now.sh and code is on github.
In this, we wrote Chart
component which will be used for any highcharts usage in the project.
This component is already set with default options needed for the Charts for the whole project.
// Chart.jsx
import React from 'react';
import Highcharts from 'highcharts/highstock';
import HighchartsReact from 'highcharts-react-official';
import noDataToDisplay from 'highcharts/modules/no-data-to-display';
noDataToDisplay(Highcharts);
class Chart extends React.Component {
getDefaultOptions() {
return {
credits: {
enabled: false
},
noData: {
position: {
x: 0,
y: 0,
align: 'center',
verticalAlign: 'middle'
}
}
};
}
render() {
const options = {
...this.getDefaultOptions(),
...this.props.options
};
return <HighchartsReact highcharts={Highcharts} options={options} />;
}
}
export default Chart;
Now when we need a PieChart
we will use this Chart
component and override the options.
// PieChart.jsx
import React from 'react';
import Chart from './Chart';
class PieChart extends React.Component {
getOptions = () => {
return {
chart: {
plotBackgroundColor: null,
plotBorderWidth: null,
plotShadow: false,
type: 'pie'
},
title: {
text: 'Browser market shares in January, 2018'
},
tooltip: {
pointFormat: '{series.name}: <b>{point.percentage:.1f}%</b>'
},
plotOptions: {
pie: {
allowPointSelect: true,
cursor: 'pointer',
dataLabels: {
enabled: true,
format: '<b>{point.name}</b>: {point.percentage:.1f} %'
}
}
},
series: [
// data
]
};
};
render() {
return <Chart options={this.getOptions()} />;
}
}
export default PieChart;
And when you look into the network tab the whole bundle is downloaded even though it is not required.
With Lazy load
Next we will be converting this to lazyload the chart component.
To do this we don’t have to change anything in Chart.jsx
.
The only change will be in PieChart.jsx
// PieChart.jsx
import React from "react";
-import Chart from "./Chart";
+const Chart = React.lazy(() =>
+ import(/* webpackChunkName: 'chart' */ "./Chart")
+);
+const Loader = () => {
+ return <div>Loading...</div>;
+};
class PieChart extends React.Component {
getOptions = () => {
return {
chart: {
plotBackgroundColor: null,
plotBorderWidth: null,
plotShadow: false,
type: "pie"
},
title: {
text: "Browser market shares in January, 2018"
},
tooltip: {
pointFormat: "{series.name}: <b>{point.percentage:.1f}%</b>"
},
plotOptions: {
pie: {
allowPointSelect: true,
cursor: "pointer",
dataLabels: {
enabled: true,
format: "<b>{point.name}</b>: {point.percentage:.1f} %"
}
}
},
series: [
// data
]
};
};
render() {
- return <Chart options={this.getOptions()} />;
+ return (
+ <React.Suspense fallback={<Loader />}>
+ <Chart options={this.getOptions()} />;
+ </React.Suspense>
+ );
}
}
export default PieChart;
The two main changes in the above code is
React.lazy
to load the dynamic import ofChart.jsx
React.Suspense
to load and render the component. while it loads library it will show the<loader/>
given infallback
option.
Lets see the different in the bundle sizes and how they gets loaded.
In the network tab when we are in Home
route it loads only initial bundle.
And then we me navigates to Chart
route it loads the highcharts the heavy library.