An Explanation of How and Why to Use React Suspense

What is React Suspense?
Suspense is a new React feature that was announced recently at the JSConf Conference in Iceland. It aims to help with handling async operations respectively in regard to CPU power and data fetching.
Suspense allows you to defer rendering part of your application tree until some condition is met (for example, data from an endpoint or a resource is loaded).
In this article, we’ll explore Suspense and see what potential impact this feature will have on the way React apps are built.
Why React Suspense?
There’s a good chance you’ve come across SPAs that make use of a loading icon as an indicator that data is being fetched. This is a common method used to ensure good UX for apps that are fetching data from external sources. All you have to do is check if the data has been successfully fetched, and if not, show a spinner.
However, this might not scale when the data fetching process becomes complicated:
- When both the parent and child component have loading states
- When you need a component to load only after some other (child) components have been loaded
The key module that makes Suspense work is the createFetcher
function. Available on npm as the simple-cache-provider, it works as outlined below:
- In the
render()
method, read a value from the cache - If the value is already cached, the render continues like normal
- If the value is not already cached, the cache throws an error
- When the promise resolves, React continues from where it stopped
import { createResource } from 'simple-cache-provider';
const someFetcher = createResource(async () => {
const res = await fetch(`https://api.github.com/search/users?q=yomete`);
return await res.json();
});
export default someFetcher;
We create a fetcher function using createResource
from the simple-cache-provider package.
Note: The simple-cache-provider is still an experimental feature, not to be used in a production app.
When initiating createResource
, a function is passed which is expected to return a Promise. If the Promise resolves, React carries on and render the results, else, an error is thrown.
The function can then be used in a render
function to display the results.
Let’s look at an example of Suspense in action.
React Suspense Demo
The codebase for the demo can be accessed on GitHub and the live demo can be accessed here.
We’ll be using the create-react-app package to create a new React project, with some modifications. Run the command below in your terminal to generate a React app:
npx create-react-app react-suspense
This creates a folder titled react-suspense
which contains the React app. Now, let’s make the aforementioned modifications. To make use of the experimental features such as simple-cache-provider
, the React version in the package.json
file needs to be bumped up to the alpha version.
Therefore, your package.json
file (the dependencies
object) should be updated with the code snippet below:
"react": "16.4.0-alpha.0911da3",
"react-dom": "16.4.0-alpha.0911da3",
The alpha version shown above is the version of React we need to carry out our tests. Run the npm install
command to update all dependencies.
Let’s also install the simple-cache-provider
package with the terminal command below:
npm install simple-cache-provider
With all the dependencies installed, let’s go ahead and write the code that we’ll use to demo Suspense.
The idea here is to get a list of shows from the TV Maze API and then display the results using Suspense.
To begin, we’d need to do some imports in the App.js
file. The first will be to import the createResource
function in the App.js
file. This will be imported from the simple-cache-provider
:
import { createResource } from 'simple-cache-provider';
Next, we’ll import a component, not yet created, titled withCache
. This is a Higher Order Component (HOC) that helps with Suspense rendering:
import { withCache } from './components/withCache';
Create a folder, name it components
and in it create a .withCache.js
file and edit with the code below:
import React from 'react';
import { SimpleCache } from 'simple-cache-provider';
export function withCache(Component) {
return props => (
<SimpleCache.Consumer>
{cache => <Component cache={cache} {...props} />}
</SimpleCache.Consumer>
);
}
The withCache component is a Higher Order Component that connects with SimpleCache.Consumer and puts the cache over the wrapped component.
Next, we’ll navigate back to the App.js
and create the createResource
function to fetch the data:
const sleep = ms => new Promise(r => setTimeout(() => r(), ms));
const readShows = createResource(async function fetchNews() {
await sleep(3000);
const res = await fetch(`http://api.tvmaze.com/search/shows?q=suits`);
return await res.json();
});
Here’s what the createResource
function is doing exactly:
- It creates a resource fetcher (
createResource()
) which is called with a set of parameters, in this case, an async function that fetches the list of shows titledsuits
, only after ‘waiting’ for the specified duration in thesleep
function - It returns the result of the API call
It’s important to note that the sleep
function is only being used so as simulate a longer API call for this example.
With the createResource
function created, we’ll need to get the results from the async function above and then build the view to display the results. In the App.js
file, go ahead and add the code block below:
const Movies = withCache( (props) => { return ( <React.Fragment> <div className="column is-4"> <div className="movie"> <div className="movie__left"> <img src /> </div> <div className="movie__right"> <div className="movie__right__title">Name: </div> <div className="movie__right__subtitle">Score: </div> <div className="movie__right__subtitle">Status: </div> <div className="movie__right__subtitle">Network: </div> <a href target="_blank" className="movie__right__subtitle">Link</a> </div> </div> </div> </React.Fragment> ) });
In the code above, a stateless component is created and wrapped in the withCache
Higher Order Component. It returns the necessary HTML to build the view that is needed to display the results from the API.
Also, the Bulma CSS framework is being used to help with styling. That can be added to the project by adding the line of code below to the index.html
:
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/bulma/0.6.2/css/bulma.min.css">
The next course of action is to actually read the data from the createResource()
function and then spit it out into the Movies
component.
In the Movie.js
component, just before the return
function, add the line of code below:
const result = readShows(props.cache);
Here we are using readShows(props.cache)
which either resolves the promise value or throws an error. Since readShows
is the createResource function, it expects a parameter of cache
which is props.cache
in this case. The cache
is being passed from the withCache
HOC as a prop
.
The result of the API call is then stored in the result
variable. With the API result being fetched, we can now use that to populate the view:
const Movies = withCache( (props) => { const result = readShows(props.cache); return ( <React.Fragment> {result && result.length && result.map(item => ( <div className="column is-4"> <div className="movie"> <div className="movie__left"> <img src={item.show.image.original} /> </div> <div className="movie__right"> <div className="movie__right__title">{item.show.name}</div> <div className="movie__right__subtitle">Score: {item.show.rating.average}</div> <div className="movie__right__subtitle">Status: {item.show.status}</div> <div className="movie__right__subtitle">Network: {item.show.network ? item.show.network.name : 'N/A'}</div> <a href={item.show.url} target="_blank" className="movie__right__subtitle">Link</a> </div> </div> </div> )) } </React.Fragment> ) });
Remember we mentioned above, that Suspense helps with async rendering by deferring rendering part of your application tree until some data or resource has been fetched. This is very important as it can be used to display some loading message as a feedback to users who are waiting for data on the screen.
Let’s go ahead and implement this in our app:
const Placeholder = ({ delayMs, fallback, children }) => {
return (
<Timeout ms={delayMs}>
{didExpire => {
return didExpire ? fallback : children;
}}
</Timeout>
);
}
The component above accepts the following:
ms
prop, which indicates the time after which we want to see thefallback
content. This is passed to thePlaceholder
component asdelayMS
fallback
is the loading state that is shown when data is being fetchedchildren
which should be a “function as a child” or “render prop” function; this function will be called with one parameter, which indicates if the specified time elapsed
We use the Placeholder
component to capture the throw by the fetcher and know the state of the data that’s being loaded.
Pulling all of this together, you can go ahead to edit the App
component with the code block below:
export default class App extends React.Component { render() { return ( <React.Fragment> <div className="App"> <header className="App-header"> <h1 className="App-title">React Suspense Demo</h1> </header> <div className="container"> <div className="columns is-multiline"> <Placeholder delayMs={1000} fallback={<div>Loading</div>}> <Movies /> </Placeholder> </div> </div> </div> </React.Fragment> ); } }
As seen above, the Placeholder
component is the parent component to the Movies
component. The fallback props
on the Placeholder
component is sent to a nice and simple loading text.
There you have it, you can go ahead to start the app with the npm start
command and you should see Suspense in action.
Conclusion
With Suspense, you have the ability to suspend component rendering while async data is being loaded. You can pause any state update until the data is ready, and you can add async loading to any component deep in the tree without plumbing all the props and state through your app and hoisting the logic.
This results in an instantaneous and fluid UI for fast networks and an intentionally designed loading state for slow networks as opposed to a general loading state.
It’s important to note that these APIs are still in experimental mode and not suitable for production. It’s best to always stay in tune with the React team for any API changes and updates to the Suspense feature.
The codebase for the demo above can be accessed on GitHub and the live demo can be accessed here.
Full visibility into production React apps
Debugging React applications can be difficult, especially when users experience issues that are difficult to reproduce. If you’re interested in monitoring and tracking Redux state, automatically surfacing JavaScript errors, and tracking slow network requests and component load time, try LogRocket.

LogRocket is like a DVR for web apps, recording literally everything that happens on your React app. Instead of guessing why problems happen, you can aggregate and report on what state your application was in when an issue occurred. LogRocket also monitors your app's performance, reporting with metrics like client CPU load, client memory usage, and more.
The LogRocket Redux middleware package adds an extra layer of visibility into your user sessions. LogRocket logs all actions and state from your Redux stores.
Modernize how you debug your React apps — start monitoring for free.