1. What is Suspense ?

React Suspense allows you to suspend components rendering until a condition is met. While waiting, a fallback component is rendered. There are basically 2 main use cases:

code splitting: the condition is the download of a chunk of your app when the user wants to access to it,

data fetching: the condition is the download of data.

In both cases, the fallback component will likely be a loader.

To enable that, you need to wrap your component that will be suspended, with the Suspense component.

Suspense usage example

In the example above, the PokemonList component rendering can be suspended, waiting for the list of pokemons. During the suspension, a Loading component is rendered.

The loader is not rendered immediately. To have a better user experience, React adds 100ms delay, before rendering the fallback. This avoids a blink, rendering the real component just after the loader, when the resource is small or the network is fast.

Let’s see how to perform code splitting and data fetching with Suspense.

2. The present - Suspense for code splitting

What is code splitting ?

Code splitting is a technique to reduce javascript bundle size by creating multiple chunks instead of a big unique one.

A way to do that is to use bundlers such as Webpack or Parcel, that automatically split the bundle into chunks, based on dynamic import usage.

Dynamic import example

In the example above, the bundlers will create a chunk, containing PokemonList code (and its imported dependencies). At runtime, it will download the chunk, and return a Promise. This Promise resolves PokemonList component which is the default export.

How to use Suspense to display a loader ?

React comes with a support of lazy components. Instead of handling the dynamic import Promise, React.lazy allows you to use the dynamic component like a statically imported one.

Let’s see some code.

Suspense for code splitting example

React.lazy takes a function that will execute the dynamic import. It returns a component that will run this function during its first rendering. The resulting promise status is used by Suspense

to render the fallback during the chunk download (pending Promise)

to render the real PokemonList component once downloaded (resolved Promise)

We can see a real benefit to use Suspense with React.lazy for code splitting. The code feels synchronous while being asynchronous, and we don’t have to write a lot of boilerplate to manage dynamic import Promise and its component usage.

This represents the stable part of Suspense. But it isn’t limited to code splitting. React core team is working on using Suspense for data fetching. Let’s see what it might look like.

3. The future - Suspense for data fetching

Keep in mind that Suspense for data fetching is still under development and the api can change.

Data fetching loader without Suspense

Let’s see an example of how to handle data fetching loader without Suspense. To do so, let’s go deeper in our PokemonList component. We need to

Initiate an isLoading state, telling that the data is being fetched, Read the data from cache, if not present, we will fetch it, setting the isLoading state accordingly, Render the Loading or List component, depending on the isLoading state.

PokemonList component handle Loading rendering while data is being fetched

And here’s a simple implementation of PokemonService. Nothing complicated, get() function fetches the data, and sets the cache value, that is exposed via a readCache() function.

PokemonService simple implementation

We can already handle loader within our components, so why should we use Suspense in the future ?

Data fetching loader with Suspense

Let’s see how we can have the same behaviour with Suspense. We are going to read the data from cache. But if it’s not in cache, we throw a Promise fetching the data.

Read from cache throws a promise if the cache is empty

Just remember that Suspense is one of our component ancestors

PokemonList wrapped in Suspense

Suspense uses React error boundaries to catch the thrown promise, and sets a loading state for us, rendering the fallback component (Loading). When it’s resolved, the PokemonList component is then rendered again.

We can see the benefit in PokemonList code.

No more isLoading state to initiate and to set, no more useEffect to fetch the data, the code just feels synchronous. Suspense handles all the asynchronous state, and coordinates the components rendering.

One last step to make it work, we need to change our way to render our React app, to enable asynchronous capabilities.

React render with asynchronous capabilities

Throwing a fetch Promise can seem weird. If it bothers you, React core team is working on React-cache to manage resources.

Data fetching with Suspense and react-cache

Keep in mind that react-cache is still under development, the lib is unstable and the api will change.

React-cache allows you to create resources that are compatible with Suspense for data fetching. Let’s see how we can implement our PokemonService with react-cache.

PokemonService using react-cache

Let’s adapt our PokemonList component to this new service api.

PokemonList using the new PokemonService

Under the hood, react-cache will do what our old service was doing