Last updated: Apr 29, 2026
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.
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.
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.
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.