JavaScript, though written to be a simple language, can produce surprisingly complex code bases. Part of the reason is that there is a wide variety of classes and modules available. Most substantial JavaScript programs and frameworks have many dependencies, which can make a seemingly simple project accrue a large amount of code quickly.
The more code a project has, the slower the browser will load. Therefore, you often have to balance the size of your dependencies with the performance you expect out of your JavaScript. Code splitting is a useful way to strike this balance.
Many JavaScript frameworks bundle all dependencies into one large file. This makes it easy to add your JavaScript to an HTML page. The bundle requires only one link tag, and there are fewer calls needed to set up the page since all the JavaScript is in one place. In theory, bundling JavaScript in this way should speed up page loads and lower the amount of traffic that page needs to handle.
At a certain point, however, a bundle grows to a certain size at which the overhead of interpreting and executing the code slows the page load down instead of speeding it up. This tipping point is different for every page, and you should test your pages to figure out where this is. There isn’t a general guideline — it all relies on the dependencies being loaded.
The key to code splitting is figuring out which parts of a page need to use different JavaScript dependencies. Code splitting allows you to strategically omit certain dependencies from bundles, then insert them only where they are needed. This means they are also not loaded until they are needed — loading JavaScript only when it is needed speeds up the page’s load time.
This may seem counterintuitive at first since the page is still loading the same amount of code, but the difference happens because the page may not execute all the code it loads. For example, if a dynamic element in a page requires a large dependency, that dependency could cause a noticeable page lag. If the dependency is loaded only when the dynamic element is used, however, the dependency may be small enough where no lag is generated.
Code splitting is a common practice in large React applications, and the increase in speed it provides can determine whether a user continues using a web application or leaves. Many studies have shown that pages have less than three seconds to make an impression with users, so shaving off even fractions of a second could be significant. Therefore, aiming for three seconds or less of load time is ideal.
There are several ways to implement code splitting in React. Different bundlers work in different ways, but React has multiple methods to customize bundling regardless of the bundler used.
Perhaps the simplest way to split code in React is with the dynamic “import” syntax. Some bundlers can parse dynamic import statements natively, while others require some configuration. The dynamic import syntax works for both static site generation and server-side rendering.
Dynamic imports use the then
function to import only the code that is needed. Any call to the imported code must be inside that function.
import("./parseText").then(parseText => { console.log(parseText.count("This is a text string", "text")); });
React.lazy allows for lazy loading of imports in many contexts. It is not yet available for server-side rendering, but its diversity of functions makes up for that. The React.lazy function allows you to dynamically import a dependency and render that dependency as a component in a single line of code. The component must be rendered inside another component that shows fallback content if the dynamic import fails.
This can include error content, although this is not required. If error content is included, it must be included in a special type of component called an Error Boundary. The Error Boundary component must be above any components that are dynamically loaded to make sure they display properly.
import React, { Suspense } from 'react'; const comp1 = React.lazy(() => import('./comp1')); function MyComponent() { return ( <div> <Suspense fallback={<div>Loading...</div>}> <comp1 /> </Suspense> </div> ); }
The goal of code splitting in React is to give users the best dynamic experience possible. This makes choosing the places within code to dynamically load dependencies an important task.
React provides a good default place to start in its routes since users expect a slight delay when switching between pages. Dynamic code splitting on routes usually involves some dependencies of its own, but since users often expect some form of page transition, a little purposeful delay during loading might be useful.
React also allows you to split your code based on components, rather than on raw functionality. Sometimes a developer will have a group of React components that create a key piece of their page, and they will use that same group of components multiple times. When this happens, lazy loading the components’ dependencies every time the components are needed could slow down the page.
The React-loadable function gives you the ability to create a customized loader that dynamically imports the chunk of code required only once. Wrapping the dynamic imports in a loader prevents them from being included in a page load bundle.
import Loadable from 'react-loadable'; function ProgressDiv() { return <div>Div Loading In Progress...</div>; } const LoaderContainerComponent = Loadable({ loader: () => import('./loadable-another-component'), LoadingComponent: ProgressDiv }); class MyComponent extends React.Component { render() { return <LoaderContainerComponent/>; } }
The same process can be used whether loading one or even a group of components many times. Loading a group of components often requires multiple dynamic imports and extracting the component types needed, but the basic idea is the same.
Even after chunking is optimized, it can be difficult to keep track of a chunk’s contents. By default, chunks have randomly generated names that can make debugging difficult.
Some bundlers, like Webpack, let you name your chunks by putting comments into your component loader implementations. Naming chunks can help you figure out how much overhead is required to load each one. Loading too many small chunks could slow down a page more than having a single large chunk, even if everything in the large chunk isn’t used every time the page loads.
React eliminates a lot of manual work from the code splitting process, allowing developers to focus on creating robust programs. Code splitting is an essential part of the modern web, so it’s no wonder that so many developers depend on it within React.
Install LogRocket via npm or script tag. LogRocket.init()
must be called client-side, not
server-side
$ npm i --save logrocket // Code: import LogRocket from 'logrocket'; LogRocket.init('app/id');
// Add to your HTML: <script src="https://cdn.lr-ingest.com/LogRocket.min.js"></script> <script>window.LogRocket && window.LogRocket.init('app/id');</script>
Hey there, want to help make our blog better?
Join LogRocket’s Content Advisory Board. You’ll help inform the type of content we create and get access to exclusive meetups, social accreditation, and swag.
Sign up nowExplore use cases for using npm vs. npx such as long-term dependency management or temporary tasks and running packages on the fly.
Validating and auditing AI-generated code reduces code errors and ensures that code is compliant.
Build a real-time image background remover in Vue using Transformers.js and WebGPU for client-side processing with privacy and efficiency.
Optimize search parameter handling in React and Next.js with nuqs for SEO-friendly, shareable URLs and a better user experience.