---
title: React 18
description: Concurrent rendering, automatic batching, transitions, and new hooks landed in React 18.
tokens: ~842
---

# React 18

React 18 shipped in March 2022. The headline change is **concurrent rendering** — a behind-the-scenes mechanism that lets React prepare multiple versions of the UI at the same time. Rendering becomes interruptible: React can pause, resume, or abandon work to keep the interface responsive. All other v18 features build on top of this foundation.

## New Root API

`ReactDOM.render` is replaced by `createRoot` (and `ReactDOM.hydrate` by `hydrateRoot`). The new API is required for any concurrent feature to work.

```tsx
import { createRoot } from "react-dom/client";
import App from "./App";

const root = createRoot(document.getElementById("root")!);
root.render(<App />);
```

Existing apps can upgrade to the new root with no other changes — concurrent features are opt-in.

## Automatic Batching

In React 17, state updates were only batched inside React event handlers. React 18 batches updates everywhere — inside `setTimeout`, `Promise` callbacks, native event listeners, and any other context. Fewer re-renders happen by default.

```tsx
setTimeout(() => {
  setCount((c) => c + 1);
  setFlag((f) => !f);
  // React 17: two re-renders
  // React 18: one re-render (batched)
}, 1000);
```

## Transitions

`useTransition` and `startTransition` mark state updates as non-urgent. Urgent updates (typing, clicking) can interrupt a transition in progress so the UI never feels sluggish.

```tsx
import { useState, useTransition } from "react";

function Search() {
  const [query, setQuery] = useState("");
  const [results, setResults] = useState<string[]>([]);
  const [isPending, startTransition] = useTransition();

  function handleChange(e: React.ChangeEvent<HTMLInputElement>) {
    setQuery(e.target.value); // urgent — update input immediately

    startTransition(() => {
      setResults(filterLargeList(e.target.value)); // non-urgent
    });
  }

  return (
    <>
      <input value={query} onChange={handleChange} />
      {isPending ? <p>Loading…</p> : <ResultList items={results} />}
    </>
  );
}
```

## New Hooks

| Hook                   | Purpose                                                                 |
| ---------------------- | ----------------------------------------------------------------------- |
| `useTransition`        | Mark updates as non-urgent; returns `[isPending, startTransition]`      |
| `useDeferredValue`     | Defer re-rendering a value until higher-priority updates finish         |
| `useId`                | Generate a stable unique ID safe for SSR hydration                      |
| `useSyncExternalStore` | Subscribe to external stores with tearing protection under concurrency  |
| `useInsertionEffect`   | Inject styles before layout effects run; designed for CSS-in-JS libs    |

## Streaming SSR with Suspense

`renderToPipeableStream` (Node) and `renderToReadableStream` (Web Streams) replace `renderToString` for server rendering. They stream HTML progressively and support `<Suspense>` boundaries on the server — slow sections show a fallback first and swap in when ready, without blocking the rest of the page.

## Strict Mode Changes

In development, React 18 Strict Mode mounts every component twice (mount → unmount → mount) to surface side-effects that break with reusable state. This helps prepare for future features where React may unmount and remount parts of the tree to preserve state.