Taking the Economical Approach: Code-Splitting in React

React Logo

One of the predominant concerns of front-engineering is the achievement of optimal performance in an application. Decisions — both on a structural level and on a code level — are made with this in mind. One of the reasons why React is so popular with engineers and businesses is that it encompasses a wide range of features that enable performance optimisation. These include the library’s light weight, its broad suite of built-in hooks and memoisation, to name just a few.

One feature of React which is very effective in providing optimisation of performance is its built-in code-splitting feature.

Dynamic Imports

Typically with React, all code in an application is bundled into one file after a build, which is then served up to the browser for execution. This is an efficient concept in general. On initially loading the application, the browser has all of the code that it will require, and subsequent actions do not require separate calls to the server. However, if the bundled JavaScript file is very large (for example, in a large enterprise application), then this can weigh down on performance.

Code-Splitting

React’s code-splitting feature allows an engineer to manage this challenge by considerably reducing the size of the bundled JavaScript file. This is achieved by not including components that are not strictly necessary for the basic operation of the application. Instead, as the browser requires various other components as the user navigates through the application, these components are dynamically loaded upon demand in an asynchronous manner.

Instead of statically importing all required modules at the beginning of a file (which are then included in the bundled JavaScript file), a component can be imported inside of the execution of a function. Thus, it will not be downloaded to the browser unless the function is called.

React.lazy()

React’s way of enabling dynamic importing of a component is by means of its lazy() function. An anonymous function is passed as the argument to React.lazy(), the callback of which is a dynamic import of the component (which must be a default export). The returned value of the lazy() function may be assigned to a variable, which is in turn included in the JSX. An example of such an import is outlined below:

const dynamicallyImportedComponent = React.lazy(() => import('./component'));

Since a dynamic import returns a promise, the component which is carrying out the dynamic import must handle its pending state. It does this by wrapping the imported component inside of React’s Suspense component (see my article on React’s Suspense component here). Suspense enables lazy loading of data by rendering a fallback element until the data — in this case the dynamically imported component — is loaded. Indeed, multiple dynamically imported components may be included within the Suspense component.

Route-Based Code-Splitting

Since the bundling of JavaScript files prevents the need for making multiple calls to a server, code-splitting should not be carried out in such a way as to undermine all of those benefits. A recommended manner in which to split code, so as not to impede upon user experience, is to implement route-based code-splitting. This is predicated upon the supposition that users expect a certain lag in time when transitioning between URLs, and so would be more accepting of a lapse of a number of milliseconds whilst dynamically importing components in this context.

In order to implement this route-based code-splitting, page components are dynamically imported inside of aSuspense component, which is in turn contained within the React Router component.

Conclusion

By managing and splitting code in the aforementioned manner, application performance is greatly enhanced, as code which is not necessarily required by the user will not contribute to any performance overhead. Only if needed will this code be asynchronously downloaded, and if handled smartly, as in the case of route-based code-splitting, the user should not be cognisant of any slow-down. The combination of dynamic imports with React’s lazy() function and React’s Suspense component, ensures that application performance can be afforded the priority that it warrants in product development.

JavaScript Enthusiast and Professional