Introduction
As React applications grow in size, number of components and third party libraries increase, loading all JavaScript at once can slow down performance, leading to increased load times and a poor user experience. Code splitting and lazy loading are techniques that help optimize React apps by loading only the necessary code when needed. This guide will walk you through the concepts, benefits, and implementation of code splitting and lazy loading in React.
What is Code Splitting?
As we know our browser receives a whole chunk of javaScript for the server, and the more advanced our application becomes, this chunk gets huge in size. Code splitting is a technique that breaks the application's JavaScript bundle into smaller chunks and ships them to the browser as required by the user. Instead of loading the entire application at once, the browser loads only the required parts, reducing initial load time.
Benefits of Code Splitting:
Faster Initial Load: Loads only essential code, improving page speed.
Efficient Resource Usage: Downloads JavaScript files as needed, reducing memory usage.
Improved User Experience: Pages render quickly without unnecessary delays.
What is Lazy Loading?
Lazy loading is a strategy where specific parts of a React application are loaded only when required. Instead of downloading everything at once, we only load the necessary resources when they are needed but this performance of the application is optimized. It works in conjunction with code splitting to dynamically import components when they are needed. We can use React.lazy without any additional configuration and
Benefits of Lazy Loading:
Reduces Bundle Size: Only loads components when required, keeping initial bundle size minimal.
Optimized Performance: Enhances app responsiveness by deferring non-essential component loading.
Saves Bandwidth: Downloads assets only when necessary, benefiting users with limited internet speeds.
Implementing Code Splitting and Lazy Loading in React
React provides built-in support for code splitting using React.lazy and React.Suspense.
React.lazy() is a powerful feature for enhancing React application performance. It enables dynamic component imports, reducing the initial bundle size and improving overall efficiency.
React.Suspense() is a component that allows you to handle the loading state of dynamically imported components. It displays a fallback UI while the lazy-loaded component is being fetched, improving the user experience. We wrap the lazy-loaded components in the Suspense component.
Using React.lazy for Component Lazy Loading
import React, { Suspense, lazy } from "react";
const LazyComponent = lazy(() => import("./LazyComponent"));
function App() {
return (
<div>
<h1>React Code Splitting Example</h1>
<Suspense fallback={<div>Loading...</div>}>
<LazyComponent />
</Suspense>
</div>
);
}
export default App;
Explanation:
lazy(() => import("./LazyComponent")) dynamically imports the component.
Suspense provides a fallback UI (Loading...) while the component loads.
Code Splitting with React Router
For route-based splitting, React.lazy can be combined with React Router: We can lazy load an entire page of our application. By doing this we are breaking down our application into chunks per route, then load that chunk when the user navigates to that route. To do this we simply convert all route components in our app to lazy components and wrap them in a suspense component.
import React, { Suspense, lazy } from "react";
import { BrowserRouter as Router, Route, Routes } from "react-router-dom";
const Home = lazy(() => import("./Home"));
const About = lazy(() => import("./About"));
function App() {
return (
<Router>
<Suspense fallback={<div>Loading...</div>}>
<Routes>
<Route path="/" element={<Home />} />
<Route path="/about" element={<About />} />
</Routes>
</Suspense>
</Router>
);
}
export default App;
Optimizing with Webpack
Webpack automatically handles code splitting when using dynamic imports (import()), but you can optimize it further:
Use optimization.splitChunks: Ensures common dependencies are split into separate files.
Enable webpack-bundle-analyzer: Helps visualize and optimize bundle sizes.
Dynamic Import with Webpack
const component = import("./Component").then((module) => module.default);
Lazy Loading Images
Lazy loading can also be applied to images to improve performance.
import React, { useState, useEffect } from "react";
function LazyImage({ src, alt }) {
const [imageSrc, setImageSrc] = useState(null);
useEffect(() => {
const img = new Image();
img.src = src;
img.onload = () => setImageSrc(src);
}, [src]);
return <img src={imageSrc || "loading-placeholder.jpg"} alt={alt} />;
}
export default LazyImage;
Lazy Loading with Intersection Observer
The IntersectionObserver API is a web API that allows us to observe the changes in the interaction of the target element with an ancestor element or the viewport. It can be used to lazy load components when they come into view.
import React, { useState, useEffect, Suspense, lazy } from "react";
const LazyComponent = lazy(() => import("./LazyComponent"));
function LazyWrapper() {
const [isVisible, setIsVisible] = useState(false);
const ref = React.useRef();
useEffect(() => {
const observer = new IntersectionObserver(
([entry]) => entry.isIntersecting && setIsVisible(true),
{ threshold: 0.1 }
);
if (ref.current) observer.observe(ref.current);
return () => observer.disconnect();
}, []);
return (
<div ref={ref}>
{isVisible && (
<Suspense fallback={<div>Loading...</div>}>
<LazyComponent />
</Suspense>
)}
</div>
);
}
export default LazyWrapper;
Best Practices for Code Splitting and Lazy Loading
Split large components: Identify large components and split them into smaller chunks.
Use lazy loading for routes: Load routes only when users navigate to them.
Provide meaningful fallbacks: Use skeleton loaders instead of simple loading text.
Avoid excessive splitting: Too many small chunks can lead to additional HTTP requests, affecting performance.
Conclusion
Code splitting and lazy loading are essential techniques to enhance React application's performance. By strategically implementing React.lazy, Suspense, and Webpack optimizations, developers can significantly improve load times, reduce bundle sizes, and create a seamless user experience. Start optimizing your React app today and make it more efficient and scalable!