Understanding React Suspense

23. February, 2023 3 min read Develop

It all comes down to fallbacks

React 18 introduced concurrent rendering, and Suspense is one of the first features that take advantage of it.

The Suspense component allows you to display a fallback until the children in the tree have finished loading. This approach allows for a smoother user experience and fewer re-renders.

In a nutshell

You wrap your component in Suspense and provide a fallback:

<Suspense fallback={<LoadingArticles />}>
  <Articles />
</Suspense>

React will display the <LoadingArticles /> component until <Articles />, or any other child inside Suspense, has finished loading.

However, you require Suspense-enabled data sources for this to work. These might be frameworks such as Relay or Next.js.

Suspense does not work inside an Effect when fetching data. Therefore, I will use React’s lazy loading component to create an example of how it works in this article.

Using lazy loading

A well-built React app splits up its code base or functionality into several smaller bundles and serves only the code the end user needs. This approach is typically achieved through a package builder such as Webpack. Depending on the users’ requests, lazy loading individual bundles increases the speed and user experience.

Let’s build an example. I will create an article component that may fetch articles from a data source. I keep this very simple without the fetching part:

// Articles.tsx
import React from 'react';

const Articles = () => {
  return <p>A list of articles.</p>;
};

export default Articles;

Remember that Reacts Suspense will not incorporate the data fetching inside an effect. Our articles component must take care of that independently. However, it will show our loading component before the article’s data is fetched.

Let’s have a look at our demo app:

// App.tsx
import React, { lazy } from 'react';

const Articles = lazy(async () => {
  await new Promise(resolve => setTimeout(resolve, 2000));

  return import('./Articles');
});

export const DemoAppLoading = () => <p>🌀 Loading...</p>;

export const DemoApp = () => {
  return <Articles />;
};

The example includes a two-second loading delay, so you can better test the behaviour. Finally, we implement this into our React app:

// index.tsx
import React, { Suspense } from "react";
import ReactDOM from "react-dom/client";

import { DemoApp, DemoAppLoading } from "./App";

const root = ReactDOM.createRoot(
  document.getElementById("root") as HTMLElement
);
root.render(
  <React.StrictMode>
    <Suspense fallback={<DemoAppLoading />}>
      <DemoApp />
    </Suspense>
  </React.StrictMode>
);

DemoAppLoading is shown until the App.tsx bundle is loaded. This setup may seem overkill for a small application like this, but keep in mind that applications can grow large relatively fast.

Conclusion

React offers excellent documentation about Suspense with various examples. The feature also allows for nesting and other minor tricks to help you. It is a great tool to enhance the user experience for your next application.

‘Till next time!