DEV Community

Mohamed Idris
Mohamed Idris

Posted on

Code Splitting in React with `lazy` and `Suspense`

In React, code splitting allows us to load parts of our application only when they’re actually needed. Instead of downloading all JavaScript upfront, we can defer loading less important or heavy components until the user interacts with the UI.

This is especially useful for large applications or components that users may never see.


Why Code Splitting Matters

Benefits

  1. Improved performance
    Smaller initial JavaScript bundles mean faster page load times, especially on slow networks or low-end devices.

  2. Better user experience
    Only essential code is loaded on first render. Additional code is fetched later, exactly when the user needs it.


The Problem: Unnecessary Network Requests

Before using React.lazy and Suspense, the SlowComponent was already loaded in the Network tab, even though:

  • The toggle button was false
  • The user never opened or saw the component

This means the browser downloaded code that the user might never use.

Before (without lazy loading)

SlowComponent is loaded immediately on page load:

Browser Network tab showing SlowComponent JavaScript file loaded on initial page load, even though the toggle is false

This is wasted bandwidth and unnecessary work.


The Solution: React.lazy + Suspense

React provides lazy and Suspense to load components only when they’re rendered.

Basic Example

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

const DataComponent = lazy(() => import('./DataComponent'));

function MyComponent() {
  return (
    <Suspense fallback={<div>Loading...</div>}>
      <DataComponent />
    </Suspense>
  );
}
Enter fullscreen mode Exit fullscreen mode

Here:

  • DataComponent is not loaded until it’s actually rendered
  • A fallback UI is shown while the component is loading

Practical Example with a Heavy Component

In this example:

  • SlowComponent is heavy
  • It should only load when the user clicks a button
  • We also use useTransition to keep the UI responsive
import { useState, useTransition, lazy, Suspense } from 'react';

const SlowComponent = lazy(() => import('./SlowComponent'));

const App = () => {
  const [text, setText] = useState('');
  const [items, setItems] = useState([]);
  const [isPending, startTransition] = useTransition();
  const [show, setShow] = useState(false);

  const handleChange = (e) => {
    setText(e.target.value);

    startTransition(() => {
      const newItems = Array.from({ length: 5000 }, (_, index) => (
        <div key={index}>
          <img src="/vite.svg" alt="" />
        </div>
      ));
      setItems(newItems);
    });
  };

  return (
    <section>
      <form className="form">
        <input
          type="text"
          className="form-input"
          value={text}
          onChange={handleChange}
        />
      </form>

      <h4>Items Below</h4>

      {isPending ? (
        'Loading...'
      ) : (
        <div
          style={{
            display: 'grid',
            gridTemplateColumns: '1fr 1fr 1fr',
            marginTop: '2rem',
          }}
        >
          {items}
        </div>
      )}

      <button onClick={() => setShow(!show)} className="btn">
        Toggle
      </button>

      {show && (
        <Suspense fallback={<h4>Loading...</h4>}>
          <SlowComponent />
        </Suspense>
      )}
    </section>
  );
};

export default App;
Enter fullscreen mode Exit fullscreen mode

What Changed in the Network Tab?

After Applying lazy

Now, SlowComponent is not loaded on initial page load:

Browser Network tab showing that SlowComponent JavaScript file is not loaded after page load when lazy loading is applied

Loaded Only on User Action

The component is fetched only when the user clicks the toggle button:

Browser Network tab showing SlowComponent JavaScript file being requested after the user clicks the toggle button

This is exactly what we want.


Key Takeaways

  1. Lazy loading
    SlowComponent is not downloaded until it’s rendered.

  2. Suspense
    Handles loading states while the component is fetched.

  3. Better performance
    No unnecessary JavaScript is loaded upfront.

  4. User-driven loading
    Code is fetched only when the user actually needs it.


Optional: Wrapping More with Suspense

If your application contains multiple lazy-loaded components, you can wrap a larger portion of your component tree with Suspense.
In many cases, it’s common to wrap the entire return statement so all lazy components share the same loading fallback.

<Suspense fallback={<h4>Loading...</h4>}>
  {/* one or more lazy-loaded components */}
</Suspense>
Enter fullscreen mode Exit fullscreen mode

This approach keeps your code cleaner and ensures a consistent loading experience across the app, while still benefiting from code splitting.


Conclusion

Before using React.lazy and Suspense, SlowComponent was downloaded even when the toggle was false, meaning users paid the cost for code they might never see.

By using code splitting, we:

  • Reduce the initial bundle size
  • Improve performance
  • Load code only when it’s truly needed

Credits: John Smilga’s course

Top comments (0)