Frontend Development 12 min read

What’s New in React 18? A Deep Dive into Concurrent Rendering and Suspense

React 18 introduces a progressive upgrade path with concurrent rendering, automatic batching, a new Root API, startTransition, and an improved Suspense SSR architecture, allowing developers to adopt new features with minimal code changes while enhancing performance and enabling selective hydration and streaming HTML.

Taobao Frontend Technology
Taobao Frontend Technology
Taobao Frontend Technology
What’s New in React 18? A Deep Dive into Concurrent Rendering and Suspense

React 18 Overview

After a long period of React 16 development, the React team released version 17 as a transition release aimed at reducing upgrade costs. The key change was moving event delegation from the document to the root DOM container, making it possible to run multiple React versions side‑by‑side.

React 18 continues the "progressive upgrade" strategy, adding optional features such as concurrent rendering that do not immediately break existing component behavior. Upgrading can be done with minimal or no code changes.

New Root API

In React 18,

ReactDOM.render()

is deprecated (legacy) and replaced by

ReactDOM.createRoot()

. The new API enables creating multiple root nodes and supports concurrent rendering.

<code>import ReactDOM from 'react-dom';
import App from 'App';

// Legacy API
ReactDOM.render(&lt;App /&gt;, document.getElementById('root'));

// New API
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(&lt;App /&gt;);
</code>

Automatic Batching

Batching groups multiple state updates into a single re‑render for better performance. React 17 only batches updates that occur during a browser event. React 18 also batches updates that happen after the event, such as in async callbacks.

<code>function App() {
  const [count, setCount] = useState(0);
  const [flag, setFlag] = useState(false);

  function handleClickPrev() {
    setCount(c => c - 1); // not rendered yet
    setFlag(f => !f);    // not rendered yet
    // React re‑renders once at the end (automatic batching)
  }

  function handleClickNext() {
    fetchSomething().then(() => {
      setCount(c => c + 1); // causes a re‑render in React 17
      setFlag(f => !f);    // causes a re‑render in React 17
    });
  }

  return (
    <div>
      <button onClick={handleClickPrev}>Prev</button>
      <button onClick={handleClickNext}>Next</button>
      <h1 style={{color: flag ? "blue" : "black"}}>{count}</h1>
    </div>
  );
}
</code>

If you need an immediate DOM update, use

ReactDOM.flushSync()

(not recommended for regular use).

<code>import { flushSync } from 'react-dom';

function handleClick() {
  flushSync(() => {
    setCounter(c => c + 1);
  });
  // DOM is updated here
  flushSync(() => {
    setFlag(f => !f);
  });
}
</code>

Impact on Hooks and Classes

Hooks are unaffected.

Class components are mostly unaffected, but reading state between two

setState

calls can behave differently due to batching.

New Suspense SSR Architecture

React 18 introduces a streaming HTML approach combined with selective hydration. The server streams the initial HTML (e.g., navigation, sidebar, main content) while placeholders (spinners) are sent for async components like comments. When the async data arrives, React injects the component HTML via a tiny inline script.

<code>&lt;main&gt;
  &lt;nav&gt;…&lt;/nav&gt;
  &lt;aside&gt;…&lt;/aside&gt;
  &lt;article&gt;Hello world&lt;/article&gt;
  &lt;section id="comments-spinner"&gt;
    &lt;img src="spinner.gif" alt="Loading..."&gt;
  &lt;/section&gt;
&lt;/main&gt;

&lt;div hidden id="comments"&gt;
  &lt;p&gt;First comment&lt;/p&gt;
  &lt;p&gt;Second comment&lt;/p&gt;
&lt;/div&gt;
&lt;script&gt;
  document.getElementById('comments-spinner')
    .replaceChildren(document.getElementById('comments'));
&lt;/script&gt;
</code>

This streaming model breaks the "all‑or‑nothing" limitation of traditional SSR.

startTransition API

React 18 adds

startTransition

to separate urgent updates (e.g., typing) from transition updates (e.g., fetching and displaying results). Transition updates are lower priority and can be interrupted, improving UI responsiveness.

<code>import { startTransition } from 'react';

// Urgent: show typed input immediately
setInputValue(input);

// Transition: update search results
startTransition(() => {
  setSearchQuery(input);
});
</code>

Compared with

setTimeout

,

startTransition

schedules updates earlier and allows React to pause them if needed, avoiding UI blocking.

When to Use startTransition

Slow rendering tasks (complex UI)

Slow network requests (data fetching)

References

Official React blog posts and discussion threads provide further details on the new APIs and migration strategies.

ReactConcurrent RenderingSuspenseReact 18Automatic BatchingstartTransition
Taobao Frontend Technology
Written by

Taobao Frontend Technology

The frontend landscape is constantly evolving, with rapid innovations across familiar languages. Like us, your understanding of the frontend is continually refreshed. Join us on Taobao, a vibrant, all‑encompassing platform, to uncover limitless potential.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.