In this blog, we will try to understand what code splitting is and why we should implement it. With Code splitting, we can reduce the initial bundle size, app loading time, and enhance its performance and optimise our code.
When we create an application, we write code in several files. It includes many modules and third-party libraries. When we build, it converts these many files into a single large file(bundle), well, not literally, but for understanding, yes. And this bundle is used by a web browser to load the application.
Initially, when our application is small, the bundle size is small. As the application grows and becomes more complex, the bundle size grows simultaneously. And the bigger the bundle size is, the more time it takes for the application to load. Therefore, we can say that a bigger bundle size is an issue and affects the application performance.
Now, what if there is a definite way to load the necessary files to start the application and later load other files, as well, when required. This way, we can keep our initial bundle size small and get our applications to load faster.
React has this feature, It is called Code-Splitting. Code splitting is a way/feature to split up your code from a large file or component into various bundles. These can then be requested on demand or in parallel. Some common use cases of code/bundle splitting and lazy loading, are:
- As the user moves to a new view of the application, you can lazy load more code.
- Loading may also be tied to a particular movement, such as scrolling or pressing a button.
- You might also try to guess what the user would try to do next and load the code accordingly.
- As the user attempts to access the functionality, it will already be there. Now we'll see how code splitting works and how you can do it well.
Code Splitting in a React Codebase
Some common ways to do code splitting are listed below:
1. Dynamic imports using Webpack
Webpack allows you to import modules during runtime dynamically. Here is an example. Whenever we import a module or third party library, we generally import it like this and it imports the file in a synchronous way.
import { homeComponent } from './home;
It means that the initial bundle file will have this import. Here comes the dynamic import method to import files asynchronously.
import('./home).then(home => { console.log(home.renderHome())})
This syntax will let the Webpack file that's to be code split and bundled be generated accordingly.
2.Using React.lazy and React.Suspense
Another way of splitting the code is using the React.lazy() method.This is a feature provided directly by React. This function lets you render a dynamic import as a regular component. It means that we can define components that can be imported dynamically in the application. This helps us reduce the bundle size because we are delaying the loading of the component that might be used later in the application. Let's understand this by an example.
import React, { useState } from 'react';
import homeComponent from './home;
export default function App() {
const [showDetails, setShowDetails] = useState(false);
return (
<>
<h1>Home</h1>
<button onClick={() =>
setShowDetails(true)}>Show Details
</button>
{showDetails ? <homeComponent /> : null }
</>
)};
As we can see in the above example that component homeComponent will only render when the user clicks on the button Show Details. Here we can use lazy loading to load the component homeComponent. Let’s do it with the help of React.lazy(). This method takes a function that calls a dynamic import().
const ProjectDetails = React.lazy(() => import('./home));
But there is a catch, what if our import takes time to load? The React.Suspense comes into the picture. The lazy component is always used within the Suspense component. It allows us to specify loading indicators as fallback content until our lazy component is ready to render.
import React, { Suspense } from 'react';
const OtherComponent = React.lazy(() => import('./OtherComponent'));
function MyComponent() {
return (
<div>
<Suspense fallback={<div>Loading...</div>}>
<OtherComponent />
</Suspense>
</div>
)}
The fallback props take any React element that we want to show while the lazy component is loading. You can also use a single Suspense component to wrap several lazy components.
import React, { Suspense } from 'react';
const OtherComponent = React.lazy(() => import('./OtherComponent'));
const AnotherComponent = React.lazy(() => import('./AnotherComponent'));
function MyComponent() {
return (
<div>
<Suspense fallback={<div>Loading...</div>}>
<OtherComponent />
<AnotherComponent />
</Suspense>
</div>
)}
3.Route Level Splitting
Now we know how to split our code to reduce the bundle size, so we can do it in the application so it doesn’t hinder the way the user interacts with the application.
Therefore, routes are a good place to start with because generally, users are familiar with delays when they transit from one route to another.
Let's see how we can set up route-based code splitting into your app using libraries like React Router with React.lazy.
import React, { Suspense, lazy } from 'react';
import { BrowserRouter as Router, Routes, Route } from 'react-router-dom';
import Home from "./home";
const Profile = lazy(() => import('./profile'));
const ContactUs = lazy(() => import('./contact'));
const App = () => (
<Router>
<Suspense fallback={<div>Loading...</div>}>
<Routes>
<Route path="/" element={<Home />} />
<Route path="/profile" element={<Profile />} />
<Route path="/contact" element={<ContactUs />} />
</Routes>
</Suspense>
</Router>
);
To read more about code-splitting, please check official React documentation.