How to Use Lazy Loading Component in React

I have seen many projects struggle with slow initial load times as the codebase grows.

When your bundle size gets too large, users on slower connections, like those in rural parts of the Midwest, often experience a frustrating delay.

I remember working on a large-scale logistics platform where the initial dashboard took nearly six seconds to become interactive.

By implementing lazy loading, we managed to cut that initial load time in half, significantly improving the user experience for our clients.

In this tutorial, I will show you exactly how to use lazy loading in React to keep your applications fast and responsive.

What is Lazy Loading in React?

Lazy loading is a design pattern that allows you to load parts of your application only when they are actually needed.

Instead of downloading the entire JavaScript bundle upfront, React breaks the code into smaller “chunks” that load on demand.

I like to think of it like a restaurant in New York City; they don’t bring every course to your table at once.

They bring the appetizer first, then the main course, which keeps your table clear and manageable.

Method 1: Use React.lazy and Suspense for Component Loading

The most common way I implement this is by using the built-in React.lazy function combined with the Suspense component.

This approach is perfect for heavy components like data tables or complex forms that aren’t visible immediately.

Let’s look at an example involving a “Tax Calculator” component, which might contain heavy logic that isn’t needed until a user clicks a specific tab.

import React, { Suspense, useState } from 'react';

// We import the heavy component lazily
const TaxCalculator = React.lazy(() => import('./TaxCalculator'));

const FinancialDashboard = () => {
  const [showCalculator, setShowCalculator] = useState(false);

  return (
    <div style={{ padding: '20px', fontFamily: 'Arial' }}>
      <h1>Small Business Financial Suite</h1>
      <p>Welcome to your financial overview for the 2024 fiscal year.</p>
      
      <button 
        onClick={() => setShowCalculator(true)}
        style={{ padding: '10px 20px', backgroundColor: '#007bff', color: 'white', border: 'none', borderRadius: '5px' }}
      >
        Open IRS Tax Estimator
      </button>

      {/* Suspense handles the loading state while the chunk is being fetched */}
      {showCalculator && (
        <Suspense fallback={<div style={{ marginTop: '20px' }}>Loading Tax Modules...</div>}>
          <TaxCalculator />
        </Suspense>
      )}
    </div>
  );
};

export default FinancialDashboard;

You can refer to the screenshot below to see the output.

Lazy Loading Component in React

In the example above, the TaxCalculator code isn’t even downloaded by the user’s browser until they click that button.

I always recommend providing a clean “fallback” UI inside the Suspense component to let the user know something is happening.

Method 2: Lazy Loading Routes with React Router

In my experience, the biggest performance gains come from applying lazy loading at the route level.

If a user visits your homepage, there is no reason for their browser to download the code for the “Settings” or “User Profile” pages.

Here is how I typically set up a clean, route-based code-splitting structure for a US-based E-commerce app.

import React, { Suspense, lazy } from 'react';
import { BrowserRouter as Router, Routes, Route, Link } from 'react-router-dom';

// Lazy loading the page components
const Home = lazy(() => import('./pages/Home'));
const ProductCatalog = lazy(() => import('./pages/ProductCatalog'));
const Checkout = lazy(() => import('./pages/Checkout'));
const Support = lazy(() => import('./pages/Support'));

const App = () => {
  return (
    <Router>
      <nav style={{ borderBottom: '1px solid #ccc', padding: '10px' }}>
        <Link to="/" style={{ margin: '0 15px' }}>Home</Link>
        <Link to="/catalog" style={{ margin: '0 15px' }}>Shop Electronics</Link>
        <Link to="/checkout" style={{ margin: '0 15px' }}>Cart</Link>
        <Link to="/support" style={{ margin: '0 15px' }}>Customer Support</Link>
      </nav>

      <div style={{ padding: '20px' }}>
        <Suspense fallback={
          <div style={{ textAlign: 'center', marginTop: '50px' }}>
            <h2>FastShipping.us is preparing your view...</h2>
          </div>
        }>
          <Routes>
            <Route path="/" element={<Home />} />
            <Route path="/catalog" element={<ProductCatalog />} />
            <Route path="/checkout" element={<Checkout />} />
            <Route path="/support" element={<Support />} />
          </Routes>
        </Suspense>
      </div>
    </Router>
  );
};

export default App;

You can refer to the screenshot below to see the output.

How to Use Lazy Loading Component in React

When I use this method, the main bundle stays tiny, ensuring the “Home” page loads almost instantly.

Each secondary page becomes its own separate .js file that only gets requested when the user navigates to that specific route.

Method 3: Lazy Loading Images within Components

While React.lazy is for components, I often encounter performance lags due to heavy images, such as high-res real estate listings.

I suggest using the native loading=”lazy” attribute for images, but for more control, we can create a “Lazy Image” component.

This ensures that images of homes in Los Angeles or Chicago don’t slow down the rest of the page logic.

import React from 'react';

const RealEstateCard = ({ imageUrl, address, price }) => {
  return (
    <div style={{ border: '1px solid #ddd', borderRadius: '8px', margin: '15px', width: '300px' }}>
      {/* Native browser lazy loading */}
      <img 
        src={imageUrl} 
        alt={address} 
        loading="lazy" 
        style={{ width: '100%', height: '200px', objectFit: 'cover', borderRadius: '8px 8px 0 0' }} 
      />
      <div style={{ padding: '10px' }}>
        <h3>{price}</h3>
        <p>{address}</p>
      </div>
    </div>
  );
};

const ListingsPage = () => {
  const listings = [
    { id: 1, address: '123 Maple St, Austin, TX', price: '$450,000', url: 'https://images.unsplash.com/photo-1568605114967-8130f3a36994' },
    { id: 2, address: '456 Oak Rd, Denver, CO', price: '$525,000', url: 'https://images.unsplash.com/photo-1570129477492-45c003edd2be' },
    // Imagine 50 more listings here...
  ];

  return (
    <div style={{ display: 'flex', flexWrap: 'wrap' }}>
      {listings.map(item => (
        <RealEstateCard key={item.id} imageUrl={item.url} address={item.address} price={item.price} />
      ))}
    </div>
  );
};

export default ListingsPage;

You can refer to the screenshot below to see the output.

Use Lazy Loading Component in React

In this scenario, the browser handles the heavy lifting, only fetching the image files as the user scrolls them into view.

Handle Errors in Lazy Loading

One thing I learned the hard way is that network requests for lazy chunks can fail if a user loses their connection.

If a chunk fails to load, your app might crash unless you wrap your Suspense component in an Error Boundary.

I always include an Error Boundary in my production apps to catch these specific “ChunkLoadError” issues gracefully.

import React from 'react';

class ErrorBoundary extends React.Component {
  constructor(props) {
    super(props);
    this.state = { hasError: false };
  }

  static getDerivedStateFromError(error) {
    return { hasError: true };
  }

  render() {
    if (this.state.hasError) {
      return (
        <div style={{ padding: '20px', color: 'red' }}>
          <h3>Oops! Something went wrong while loading this section.</h3>
          <button onClick={() => window.location.reload()}>Refresh Page</button>
        </div>
      );
    }

    return this.props.children; 
  }
}

// Usage
const HeavyReport = React.lazy(() => import('./HeavyReport'));

const ReportPage = () => (
  <ErrorBoundary>
    <React.Suspense fallback={<div>Generating your annual report...</div>}>
      <HeavyReport />
    </React.Suspense>
  </ErrorBoundary>
);

export default ReportPage;

Best Practices I Follow

Over the years, I have developed a few “rules of thumb” for when to use lazy loading.

First, don’t lazy load every single component; doing so creates too many small files, which can actually hurt performance due to HTTP overhead.

Focus on large libraries (like D3.js or Google Maps) and components that are hidden behind modals or tabs.

Second, always test your app using the “Network” tab in Chrome DevTools to ensure chunks are being created correctly.

I also recommend using named exports carefully, as React.lazy currently only supports default exports.

If you have a named export, you’ll need an intermediate file or a small wrapper to make it work.

Implementing these strategies has helped me build apps that feel snappy even on older devices across the country.

I hope this guide helps you take your React performance to the next level.

In this tutorial, I have covered the various ways you can use lazy loading in React to optimize your application.

I have also shown you how to use React.lazy and Suspense with practical, real-world examples.

You may also like to read:

51 Python Programs

51 PYTHON PROGRAMS PDF FREE

Download a FREE PDF (112 Pages) Containing 51 Useful Python Programs.

pyython developer roadmap

Aspiring to be a Python developer?

Download a FREE PDF on how to become a Python developer.

Let’s be friends

Be the first to know about sales and special discounts.