How to Enable Concurrent Mode in React

This article explains how to transition your application to React’s concurrent rendering model. You will learn how to upgrade your project to React 18, replace the legacy rendering API with the new root API to unlock concurrent capabilities, and start utilizing concurrent features like transitions to improve your application’s performance.

Understanding Concurrent React

In React 18, the development team shifted from an all-or-nothing “Concurrent Mode” to a system of opt-in “Concurrent Features.” Instead of putting the entire application into a different mode, concurrency is enabled under the hood when you upgrade to React 18 and adopt the new root API. This allows you to use specific concurrent features gradually without breaking existing code.

Step 1: Upgrade to React 18

To use concurrent features, you must first update your React dependencies to version 18 or later. Run the following command in your project terminal:

npm install react@latest react-dom@latest

If you are using TypeScript, you should also update your type definitions:

npm install @types/react@latest @types/react-dom@latest

Step 2: Switch to the New Root API

Upgrading the package version is not enough to enable concurrent rendering; you must also update your application’s entry point. React 18 introduces createRoot, which replaces the legacy ReactDOM.render API.

Legacy Root API (Before)

In React 17 and earlier, the entry point (usually index.js or main.jsx) looked like this:

import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';

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

New Root API (After)

To enable concurrent features, update your entry point to use createRoot:

import React from 'react';
import { createRoot } from 'react-dom/client';
import App from './App';

const container = document.getElementById('root');
const root = createRoot(container);

root.render(<App />);

Using the createRoot API automatically enables concurrent rendering out of the box for updates that opt into it.

Step 3: Use Concurrent Features

Once you have adopted the new root API, your application runs on the concurrent renderer. You can now start using concurrent features to keep your UI responsive during heavy updates.

Marking Updates as Transitions

The most common concurrent feature is the useTransition hook. This hook allows you to mark non-urgent state updates as “transitions,” telling React that it can interrupt the rendering of these updates if a more urgent event (like a keystroke or click) occurs.

Here is how to implement useTransition:

import React, { useState, useTransition } from 'react';

function SearchResults() {
  const [isPending, startTransition] = useTransition();
  const [searchQuery, setSearchQuery] = useState('');
  const [filteredList, setFilteredList] = useState([]);

  const handleChange = (e) => {
    // Urgent update: Show the typed input immediately
    setSearchQuery(e.target.value);

    // Non-urgent update: Wrap in startTransition
    startTransition(() => {
      // React can pause rendering this list if the user types another character
      setFilteredList(filterLargeDataset(e.target.value));
    });
  };

  return (
    <div>
      <input type="text" value={searchQuery} onChange={handleChange} />
      {isPending && <p>Updating list...</p>}
      <List items={filteredList} />
    </div>
  );
}

By upgrading your packages, switching to the createRoot API, and implementing hooks like useTransition or useDeferredValue, you successfully adopt and utilize Concurrent React.