Hackernoon logoConcurrent React Mode: Using Suspense and useTransition to Build A Better UX by@agzuniverse

Concurrent React Mode: Using Suspense and useTransition to Build A Better UX

Aswin G Hacker Noon profile picture

@agzuniverseAswin G

CS undergrad student with a love for tech. Currently doing things with JavaScript, Python and Golang.

The React JS dev team announced some exciting changes several months ago - React would be getting a "Concurrent Mode". Essentially this would allow React to perform multiple UI renders concurrently. Of course, JavaScript is single threaded and true concurrency is an illusion, but the new features will allow web apps (and Native Apps once these features hit React Native) to be much more responsive and snappy than they are now with less effort and custom code from the developer to make this happen.

Concurrent mode is now available in the experimental build of React, so let's dig in and see how to use the shiny new API.

In this post I'll be showing you how to use the

API with the
hook. There is yet another hook,
, that serves a slightly different, but equally important purpose that I'll cover in a follow up post.

But what about the existing Suspense feature?

You'll notice that the

API has been present in React since v16.6. This is in fact, the same API that is being extended to do more in the React experimental build. In React 16.6, Suspense can only be used for one purpose: code splitting and lazily loading components using

The New Way - Render as you fetch

This has been discussed a lot through talks and blogs and in the official documentation already, so I'll keep it brief - Concurrent React allows us to implement a "render as you fetch" pattern, which renders components as the data needed to populate them are fetched concurrently. React renders as much as it can without the available data, and renders the component that requires the fetched data as soon as the data becomes available. During this time these components are said to be "suspended".

The commonly used existing approaches are "fetch then render", which fetches all the data needed first before rendering the component, and "render then fetch" which renders a component and then the component itself fetches the data required to populate it's children. Both these approaches are slower and have a number of disadvantages that needed workarounds.

The Setup

For this, I'm using a basic React app I configured manually with Webpack and Babel (Click here for a guide I wrote on how to do that), with the only difference being running:

To get the experimental versions instead of installing the release versions of


This should also work with React apps created with

by replacing
with their experimental versions.

Opting in to Concurrent Mode

Since concurrent mode changes how React handles components fundamentally, you'll need to change the

line in your

ReactDOM.createRoot(document.getElementById('root')).render(<App />);

This enables concurrent mode in your app.

I've also set up my

to render a component called
inside which the demo is done.

import React from 'react';
import Data from './Data';

const App = () => {
    return (
            <p>React Concurrent Mode testing</p>
            <Data />

export default App;

The Demo

Now we create


import React, { useState, useTransition, Suspense } from 'react';
import DataDisplay from './DataDisplay';
import { dataFetcher } from './api';

const initialData = { read: () => { return { foo: "initial" } } };

const Data = () => {
    const [data, setData] = useState(initialData);
    const [count, setCount] = useState(0);
    const [startDataTransition, isDataPending] = useTransition({ timeoutMs: 2000 });

    const fetchNewData = () => {
        startDataTransition(() => {

    return (
            <Suspense fallback={<p>Loading...</p>}>
                <DataDisplay data={data} />
                <button disabled={isDataPending} onClick={() => fetchNewData()}>Click me to begin data fetch</button>
            <p>Counter: {count}</p>
            <button onClick={() => { setCount(count + 1); }}> Click me to check if the app is still responsive</button>


export default Data;

Breaking this down:

is a function that returns a "special" object that lets React know the states set as this object can be fetched as the components dependent on this state is rendered. These components are "suspended" if the data has not finished fetching. We'll look at how to create the "special object" towards the end.

shows the format of the object returned by
once the data has finished loading. It has a
function that returns the object with data we need. Ideally, the
should implement some sort of caching function for the last loaded data, but here we just use
{ foo : "initial" }

A state which while being updated/fetched causes a component to suspend, must be updated using the

hook. This hook returns a pair of values - a function that takes a callback function in which you set the state, and a boolean that lets us know when the transition is taking place.

The argument passed to

is an object that tells React how long to wait before suspending the component. To understand it, think of it this way: We have some data on screen, and we're fetching some new data to replace it. We want to show a spinner while the new data is being fetched, but it's okay for the user to see the old data for a second, or maybe half a second before the spinner is shown. This delay is mentioned in this object.

This is useful in cases when showing stale data till new data is loaded is desirable, and also to prevent the spinner from showing up for a fraction of a second (causing what is percieved as jitter) on fast data fetch operations.

Let's take a closer look at


<Suspense fallback={<p>Loading...</p>}>
    <DataDisplay data={data} />
    <button disabled={isDataPending} onClick={() => fetchNewData()}>Click me to begin data fetch</button>

Any component that should be suspended is wrapped inside the

component. Inside it's
prop, we pass the component that should be shown instead while the component inside is waiting for data. This is usually a spinner or loading indicator of some sort to visually indicate to the user something is happening, so it doesn't appear as if the page hasn't responded to the click.

Here I've used the

boolean to disable the button while data is being fetched, preventing the user from pressing the button multiple times and sending multiple requests - A nice bonus we get from the pattern.

Talking about the page remaining responsive - it does. All the JavaScript in the page continues to work while the component is suspended and data is being fetched. The counter and the button to increment it can be used to confirm this.

is a simple component that takes the data and calls it's read function and displays the result.

import React, { memo } from 'react';

const DataDisplay = ({ data }) => {
    return (

export default memo(DataDisplay);

is used here to prevent this component from re-rendering when it's parent re-renders, and is essential for concurrent mode to work.

Finally, we look at

and the other things inside

export const dataFetcher = (params) => {
    return wrapPromise(fetchData(params))

const wrapPromise = (promise) => {
    let status = "pending";
    let result;
    let suspender = promise.then(
        r => {
            status = "success";
            result = r;
        e => {
            status = "error";
            result = e;
    return {
        read() {
            if (status === "pending") {
                throw suspender;
            } else if (status === "error") {
                throw result;
            } else if (status === "success") {
                return result;

const fetchData = (params) => {
    // In a real situation, use params to fetch the data required.
    return new Promise(resolve => {
        setTimeout(() => {
                foo: 'bar'
        }, 3000);

As you can see,

simply returns
, and
is a function that makes the actual request for the data. In a real situation you'll be using
with the
passed to it, or load data from some place else. Here I'm using
to return a
object that intentionally introduces a 3 second delay before returning
{ foo : 'bar' }

is responsible for getting things to integrate with React, and should be straightforward if you've used Promises before. It returns the result if the fetch was successful, throws the error if it was not, and throws a
with "pending" state if the operation has not completed yet.

All of this put together results in this:

Initially the data shown is "Initial". Then I click the button to begin data fetch. According to our configuration, the button is disabled immediately and nothing happens for 2 seconds. Then the component suspends, showing the fallback "Loading...". Then finally the data fetching is completed and the component updates to show "bar". During this whole time the remaining app (shown by the counter here) remains active.

As an ending note,

can work without the
hook, but only if the required data is not part of the state.


It's worth saying here that the oddly specific functions in

is not too important, as it is expected that many popular data fetching libraries will support them in the future. React also does not recommend using concurrent mode in production because, well, it's still "experimental".

But it is expected that soon leveraging concurrent mode will be the de-facto way to get around lengthy operations in UI, allowing the creation of user experiences that remain consistent across many devices with vastly different processing powers and network connectivity speeds.


Join Hacker Noon

Create your free account to unlock your custom reading experience.