#What changed in React 19
AddedReact 19 is the biggest release since hooks, but most of it follows from one idea: Actions. React 18 gave us transitions for rendering; React 19 extends them to async work. A function that does async work inside a transition is an Action, and React tracks its pending state, errors, optimistic updates and form resets for you. useActionState, useOptimistic, useFormStatus and <form action={fn}> are all built on that.
Alongside Actions came a set of "finally" fixes that remove boilerplate: ref is a plain prop (no more forwardRef), <Context> is its own provider, ref callbacks can return a cleanup, <title> and <meta> can live in any component, and Server Components became stable. And a clean-up of old APIs: ReactDOM.render, string refs, propTypes and defaultProps on functions are gone.
Mental model for interviews: React 18 was about when React renders (concurrency); React 19 is about how you mutate data (Actions) and removing old ceremony.
import { createContext, useContext, useRef } from "react";
const ThemeContext = createContext("light");
// React 19: ref is a normal prop on function components.
function TextInput({ label, ref }) {
return <input aria-label={label} ref={ref} />;
}
function ThemedLabel() {
const theme = useContext(ThemeContext);
return <p>Theme: {theme}</p>;
}
export function Form({ log }) {
const inputRef = useRef(null);
return (
// React 19: <ThemeContext> is the provider (was <ThemeContext.Provider>).
<ThemeContext value="dark">
<ThemedLabel />
<TextInput label="Name" ref={inputRef} />
<button onClick={() => inputRef.current.focus()}>Focus</button>
<div
ref={(node) => {
log.push("ref attached");
return () => log.push("ref cleanup"); // React 19: ref callbacks can return a cleanup
}}
/>
</ThemeContext>
);
}<ThemeContext value="dark">renders the context object itself as a provider. In React 18 that had to be<ThemeContext.Provider value="dark">;.Providerstill works in 19 and the React team says it will be deprecated later.ThemedLabelreads it withuseContext→ "Theme: dark".TextInputtakesrefstraight from its props and puts it on the<input>. In React 18,refwas stripped from props, so this neededforwardRef((props, ref) => …). The test clicks "Focus" and the input has focus, soinputRef.currentreally is the DOM input. (forwardRefstill works in 19.3; a second test checks it.)- The callback ref on the
<div>runs on mount (ref attached) and returns a function. React 19 treats that as a cleanup and calls it on unmount (ref cleanup) instead of calling the ref withnullas React 18 did. That's why TypeScript now rejects implicit returns from ref callbacks (ref={el => (x = el)}): the returned value would be mistaken for a cleanup. - None of this needs a new way of thinking; it's the same React with less ceremony.
The headline list (React 19.0, December 5, 2024):
| Area | What's new |
|---|---|
| Actions | Async functions in startTransition, <form action={fn}>, formAction on buttons |
| New hooks | useActionState, useOptimistic, useFormStatus (react-dom), use() |
| Refs | ref as a prop, ref cleanup functions |
| Context | <Context> as provider |
| Document | <title>, <meta>, <link> hoisted to <head>; stylesheets with precedence; async scripts deduplicated; preload/preinit/preconnect/prefetchDNS |
| Server | Server Components and Server Functions ('use server') stable; react-dom/static prerender |
| Errors | One log per error instead of three; onCaughtError/onUncaughtError root options; hydration mismatch errors show a diff |
| Other | useDeferredValue(value, initialValue), full Custom Elements support |
| Removed | ReactDOM.render/hydrate, string refs, legacy context, propTypes/defaultProps on functions, createFactory, UMD builds |