Frontend Developer Software Developer || Fascinated by tech trends || Building usable systems that work on web and mobile.

React 18 Features

React 18 Features

React 18 was released in March 2022. This release focuses on performance improvements and updating the rendering engine.

React 18 Feature Quick Guide

ConceptConcurrent React
FeaturesAutomatic Batching, Transitions, Suspense on the server
APIscreateRoot, hydrateRoot, renderToPipeableStream, renderToReadableStream
HooksuseId, useTransition, useDeferredValue, useSyncExternalStore, useInsertionEffect
UpdatesStrict mode
Deprecated/discouragedReactDOM.render, renderToString

Concept :-

  • Concurrent React

Let’s say that we need to call two people – Alice and Bob. In a non-concurrent setting, we can only have one call at a time. We would first call Alice, end the call, and then call Bob.

This is fine when calls are short, but if a call with Alice has a long waiting period (such as on-hold), this can be a time sink.

Non-concurrent callImage showing that in a typical non-concurrent phone conversation, you have to wait for a call to be over before starting a new call.

In a concurrent setting, we could call Alice and, once we were put on hold, we could then call Bob.

This doesn’t mean that we are talking to two people at the same time. It just means that we can have two or more concurrent calls at the same time and decide which call is more important.

Concurrent callImage showing phone conversation between Alice and Bob can be concurrent,

by placing a call on hold and answering a more urgent call with Bob first. Similarly, in React 18 with concurrent rendering, React can interrupt, pause, resume, or abandon a render. This allows React to respond to the user interaction quickly even if it is in the middle of a heavy rendering task.

Before React 18, rendering was a single, uninterrupted, synchronous transaction and once rendering started, it couldn’t be interrupted.

Concurrency is a foundational update to React rendering mechanism. Concurrency allows React to interrupt rendering.

React 18 introduces the foundation of concurrent rendering and new features such as suspense, streaming server rendering, and transitions are powered by concurrent rendering.

Features :-

  • Automatic Batching

In React, batching helps to reduce the number of re-renders that happen when a state changes, when you call setState. Previously, React batched state updates in event handlers, for example:

const handleClick = () => {





//re-rendered once at the end.

However, state updates that happened outside of event handlers were not batched. For example, if you had a promise or were making a network call, the state updates would not be batched. Like this:

fetch(‘/network’).then( () => {

setCounter(); //re-rendered 1 times

setActive();  //re-rendered 2 times

setValue();   //re-rendered 3 times


//Total 3 re-renders

As you can tell, this is not performant. React 18 introduces automatic batching which allows all state updates – even within promises, setTimeouts, and event callbacks – to be batched. This significantly reduces the work that React has to do in the background. React will wait for a micro-task to finish before re-rendering.

Automatic batching is available out of the box in React, but if you want to opt-out you can use flushSync.

  • Transitions 

Transitions can be used to mark UI updates that do not need urgent resources for updating.

For example, when typing in a typeahead field, there are two things happening: a blinking cursor that shows visual feedback of your content being typed, and a search functionality in the background that searches for the data that is typed.

Showing visual feedback to the user is important and therefore urgent. Searching is not so urgent, and so can be marked as non-urgent.

These non-urgent updates are called transitions. By marking non-urgent UI updates as “transitions”, React will know which updates to prioritize. This makes it easier to optimize rendering and get rid of stale rendering.

You can mark updates as non-urgent by using startTransition. Here is an example of what a typeahead component would like when marked with transitions:

import { startTransition } from ‘react’;

// Urgent: Show what was typed


// Mark any non-urgent state updates inside as transitions

startTransition(() => {

  // Transition: Show the results



How are transitions different from debouncing or setTimeout?

  • startTransition executes immediately, unlike setTimeout.
  • setTimeout has a guaranteed delay, whereas startTransition’s delay depends on the speed of the device, and other urgent renders.
  • startTransition updates can be interrupted unlike setTimeout and won’t freeze the page.
  • React can track the pending state for you when marked with startTransition

Suspense on the server

React 18 introduces:

  1. Code splitting on the server with suspense
  2. Streaming rendering on the server

Client rendering vs server rendering

In a client-rendered app, you load the HTML of your page from the server along with all the JavaScript that is needed to run the page, and make it interactive.

If, however, your JavaScript bundle is huge, or you have a slow connection, this process can take a long time and the user will be waiting for the page to become interactive, or to see meaningful content.

Illustration of client rendering flow. Source: React Conf 2021 Streaming Server Rendering with Suspense by Shaundai Person https://www.youtube.com/watch?v=pj5N-Khihgc

In a client rendering flow, a user has to wait a long time before the page becomes interactive.

For optimizing the user experience and avoiding the user having to sit on a blank screen, we can use server rendering.

Server rendering is a technique where you render the HTML output of your React components on the server and send HTML from the server. This lets the user view some UI while JS bundles are loading and before the app becomes interactive.

4In a server rendering flow, we can display meaningful data to the user much faster by sending HTML from the server.

Streaming Server Rendering with Suspense

Server rendering further enhances the user experience of loading the page and reducing time to interactive.

Now what if most of your app is fast except for one part? Maybe this part loads data slowly, or maybe it needs to download a lot of JS before it gets interactive.

Before React 18, this part was often the bottleneck of the app, and would increase the time it took to render the component.

One slow component can slow down the entire page. This is because server rendering was all or nothing – you couldn’t tell React to defer loading of a slow component and couldn’t tell React to send HTML for other components.

React 18 adds support for Suspense on server. With the help of suspense, you can wrap a slow part of your app within the Suspense component, telling React to delay the loading of the slow component. This can also be used to specify a loading state that can be shown while it’s loading.

In React 18, one slow component doesn’t have to slow the render of your entire app. With Suspense, you can tell React to send HTML for other components first along with the HTML for the placeholder, like a loading spinner. Then when the slow component is ready and has fetched its data, the server renderer will pop in its HTML in the same stream.

You can add suspense to a slow server rendered component in React 18Image showing that suspense on the server can allow a slow component to show a loading state while others are fully rendered. 

This way the user can see the skeleton of the page as early as possible and see it gradually reveal more content as more pieces of HTML Arrive.

All of this happens before any JS or React loads on the page, which significantly improves the user experience and user-perceived latency.

Hooks :-

  • useId

useId is a hook for generating unique IDs that are stable across the server and client, while avoiding hydration mismatches.


  • useId is not for generating keys in a list. Keys should be generated from your data.

For a basic example, pass the id directly to the elements that need it:

For multiple IDs in the same component, append a suffix using the same id:


  • useId generates a string that includes the : token. This helps ensure that the token is unique, but is not supported in CSS selectors or APIs like querySelectorAll.
  • useId supports an identifierPrefix to prevent collisions in multi-root apps. To configure, see the options for hydrateRoot and ReactDOMServer.
  • useTransition

Returns a stateful value for the pending state of the transition, and a function to start it.

startTransition lets you mark updates in the provided callback as transitions:

isPending indicates when a transition is active to show a pending state:


  1. Updates in a transition yield to more urgent updates such as clicks.
  2. Updates in transitions will not show a fallback for re-suspended content. This allows the user to continue interacting with the current content while rendering the update.
  • useDeferredValue

useDeferredValue accepts a value and returns a new copy of the value that will defer to more urgent updates. If the current render is the result of an urgent update, like user input, React will return the previous value and then render the new value after the urgent render has completed.

This hook is similar to user-space hooks which use debouncing or throttling to defer updates. The benefits to using useDeferredValue is that React will work on the update as soon as other work finishes (instead of waiting for an arbitrary amount of time), and like startTransition, deferred values can suspend without triggering an unexpected fallback for existing content.

Library Hooks

The following Hooks are provided for library authors to integrate libraries deeply into the React model, and are not typically used in application code.

  • useSyncExternalStore 

useSyncExternalStore is a hook recommended for reading and subscribing from external data sources in a way that’s compatible with concurrent rendering features like selective hydration and time slicing.

This method returns the value of the store and accepts three arguments:

  • subscribe: function to register a callback that is called whenever the store changes.
  • getSnapshot: function that returns the current value of the store.
  • getServerSnapshot: function that returns the snapshot used during server rendering.

The most basic example simply subscribes to the entire store:

However, you can also subscribe to a specific field:

When server rendering, you must serialize the store value used on the server, and provide it to useSyncExternalStore. React will use this snapshot during hydration to prevent server mismatches:


getSnapshot must return a cached value. If getSnapshot is called multiple times in a row, it must return the same exact value unless there was a store update in between.

A shim is provided for supporting multiple React versions published as use-sync-external-store/shim. This shim will prefer useSyncExternalStore when available, and fallback to a user-space implementation when it’s not.

As a convenience, we also provide a version of the API with automatic support for memoizing the result of getSnapshot published as use-sync-external-store/with-selector.

  • useInsertionEffect

The signature is identical to useEffect, but it fires synchronously before all DOM mutations. Use this to inject styles into the DOM before reading layout in useLayoutEffect. Since this hook is limited in scope, this hook does not have access to refs and cannot schedule updates.


useInsertionEffect should be limited to css-in-js library authors. Prefer useEffect or useLayoutEffect instead.

Updates :-

  • Strict mode

Strict mode in React 18 will simulate mounting, unmounting, and re-mounting the component with a previous state. This sets the ground for a reusable state in the future where React can immediately mount a previous screen by remounting trees using the same component state before unmounting.

Strict mode will ensure components are resilient to effects being mounted and unmounted multiple times.

Deprecated/discouraged :-

  • ReactDOM.render, renderToString

Install React 18 and React DOM from npm or yarn, like this:

npm install react react-dom

Then, you’ll want to use createRoot instead of render.

In your index.js, update ReactDOM.render to ReactDOM.createRoot to create a root, and render your app using root.

Here’s what it would look like in React 17:

And here’s what it looks like in React 18: