What's New in React 19 - A complete summary of every major feature and breaking change
Recipe
React 19 is a major release that introduces server-first architecture, new hooks, and automatic optimizations. Here is the quick-reference checklist of what shipped:
Feature Category
------- --------
Server Components (RSC) Architecture
Server Actions Data mutations
Form Actions DOM integration
use() hook Data fetching / context
useActionState Form state management
useFormStatus Pending UI
useOptimistic Optimistic updates
ref as prop API simplification
Document Metadata SEO / head management
Asset Loading APIs Performance
React Compiler Auto-memoizationWhen to reach for this: Read this page first when starting a new React 19 project or planning a migration from React 18.
Working Example
// A single component that touches many React 19 features at once
import { use, useOptimistic, useActionState } from "react";
import { saveComment } from "./actions"; // server action
type Comment = { id: string; text: string };
function CommentThread({ commentsPromise }: { commentsPromise: Promise<Comment[]> }) {
// use() unwraps the promise (Suspense-aware)
const comments = use(commentsPromise);
// useOptimistic for instant feedback
const [optimistic, addOptimistic] = useOptimistic(
comments,
(state, newText: string) => [...state, { id: "temp", text: newText }]
);
// useActionState to wire up a server action
const [_state, formAction, isPending] = useActionState(
async (_prev: Comment[], formData: FormData) => {
const text = formData.get("text") as string;
addOptimistic(text);
await saveComment(text);
return [...comments, { id: crypto.randomUUID(), text }];
},
comments
);
return (
<section>
{/* Document metadata hoisted to <head> automatically */}
<title>Comments ({optimistic.length})</title>
<meta name="description" content="Live comment thread" />
<ul>
{optimistic.map((c) => (
<li key={c.id}>{c.text}</li>
))}
</ul>
<form action={formAction}>
<input name="text" required />
<button disabled={isPending}>Post</button>
</form>
</section>
);
}
// ref as prop - no forwardRef needed
function FancyInput({ placeholder, ref }: { placeholder: string; ref?: React.Ref<HTMLInputElement> }) {
return <input placeholder={placeholder} ref={ref} />;
}
export { CommentThread, FancyInput };What this demonstrates:
use()unwrapping a promise inside a componentuseOptimisticfor instant UI feedbackuseActionStatebinding a server action to a form- Document metadata (
<title>,<meta>) rendered inline refreceived as a regular prop withoutforwardRef
Deep Dive
How It Works
- Server Components run exclusively on the server and send serialized UI to the client. They can access databases, file systems, and secrets directly without exposing them to the browser.
- Server Actions are async functions marked with
"use server"that the framework turns into RPC endpoints. They can be passed to forms, called from event handlers, or invoked from transitions. - Form Actions let you pass an async function to
<form action={fn}>. React manages the pending state, error handling, and optimistic updates automatically. - use() is a new API (not technically a hook -- it can be called conditionally) that reads promises and context values. When reading a promise, it integrates with Suspense boundaries.
- useActionState replaces the experimental
useFormState. It returns[state, action, isPending]and works both on the client and with progressive enhancement on the server. - useFormStatus exposes the pending status of the nearest parent
<form>to any descendant component. - useOptimistic provides a pattern for showing an optimistic value while an async action is in flight, reverting automatically if the action fails.
- ref as prop means
forwardRefis no longer required. Function components receiverefas a regular prop.forwardRefstill works but is deprecated and will be removed in a future version. - Document Metadata tags (
<title>,<meta>,<link>) rendered anywhere in the component tree are automatically hoisted to<head>. - Asset Loading APIs (
preload,preinit,prefetchDNS,preconnect) fromreact-domlet you eagerly load fonts, scripts, and stylesheets. - React Compiler (formerly React Forget) automatically memoizes components and hooks, eliminating the need for manual
useMemo,useCallback, andReact.memoin most cases.
Variations
React 19 works in two main modes:
- Client-only SPA -- You get form actions,
use(), optimistic updates, metadata hoisting, and the compiler. No server components or server actions. - Full-stack framework (Next.js, Remix, etc.) -- You additionally get server components and server actions with streaming SSR.
TypeScript Notes
- React 19 ships updated
@types/reactand@types/react-dom(version 19+). Install them together. refis now part of the props type. If you useReact.ComponentProps<typeof MyComponent>, it will includerefautomatically.useActionStateis generic:useActionState<State>(action, initialState).- The
use()function is generic:use<T>(resource: Promise<T> | React.Context<T>): T.
Gotchas
- Hydration error improvements -- React 19 logs a single diff-style error instead of multiple cryptic messages. Fix: Upgrade to see cleaner errors, but you still must fix mismatches.
- Strict Mode double-firing -- Still present in development. Fix: Ensure effects are idempotent; this is unchanged from React 18.
- useActionState vs useFormState --
useFormStatewas renamed. Fix: Replace alluseFormStateimports withuseActionStatefrom"react"(not"react-dom"). - forwardRef deprecation --
forwardRefstill works but triggers a deprecation warning in development. Fix: Refactor to acceptrefas a regular prop. - Context as a provider --
<Context>can now be rendered directly instead of<Context.Provider>. The.Providersyntax still works but is deprecated. Fix: Replace<MyContext.Provider value={v}>with<MyContext value={v}>. - ref cleanup functions -- Ref callbacks can now return a cleanup function (like effects). Returning anything else (including
undefinedimplicitly) will be flagged. Fix: If your ref callback returns nothing, ensure it explicitly returnsundefinedor returns a cleanup.
Alternatives
| Approach | When to choose |
|---|---|
| Stay on React 18 | Large app with no immediate need for server components or new hooks |
| Incremental adoption | Add "use client" to existing components and adopt React 19 features file-by-file |
| Full rewrite with RSC | Greenfield project using Next.js 15+ or a framework with RSC support |
| React 19 client-only | SPA that benefits from use(), optimistic updates, and the compiler without server infra |
FAQs
What is the main architectural shift in React 19 compared to React 18?
- React 19 introduces a server-first architecture with Server Components (RSC) as the default rendering model
- Components run on the server by default and ship zero JavaScript to the browser unless marked with
"use client" - This is complemented by Server Actions for data mutations, eliminating the need for separate API routes in many cases
Which new hooks and APIs shipped in React 19?
use()for reading promises and context (can be called conditionally)useActionStatefor form state management (replacesuseFormState)useFormStatusfor pending UI in form descendantsuseOptimisticfor instant optimistic updates- Asset loading APIs:
preload,preinit,prefetchDNS,preconnect
Can I use React 19 features without a server framework like Next.js?
- Yes. In a client-only SPA you get form actions,
use(), optimistic updates, metadata hoisting, and the React Compiler - Server Components and Server Actions require a framework with RSC support (e.g., Next.js 15+, Remix)
What happened to useFormState? I see references to useActionState instead.
useFormStatewas renamed touseActionStatebefore the stable release- Import it from
"react", not"react-dom" - The signature is
useActionState(action, initialState)returning[state, action, isPending]
How does document metadata work inline in React 19?
Tags like <title>, <meta>, and <link> rendered anywhere in the component tree are automatically hoisted to <head>:
function Page() {
return (
<div>
<title>My Page</title>
<meta name="description" content="Hello" />
<h1>Content</h1>
</div>
);
}Do I still need forwardRef in React 19?
- No. Function components now receive
refas a regular prop forwardRefstill works but is deprecated and will be removed in a future version- Migrate by moving
reffrom theforwardRefsecond argument into the props object
Gotcha: What happens if I return something from a ref callback that is not a cleanup function?
- React 19 allows ref callbacks to return a cleanup function (like effects)
- Returning anything else (including implicitly returning
undefinedfrom an arrow function expression) will be flagged as a warning - Ensure your ref callback either returns nothing explicitly or returns a cleanup function
Gotcha: Does Strict Mode still double-fire effects in React 19?
- Yes. Strict Mode double-firing in development is unchanged from React 18
- Effects must be idempotent -- running them twice should not cause different behavior
- This is a development-only behavior and does not affect production builds
How do I type useActionState generically in TypeScript?
const [state, action, isPending] = useActionState<MyState>(
async (prev: MyState, formData: FormData) => {
// return new state
return { ...prev, updated: true };
},
{ updated: false }
);useActionStateis generic:useActionState<State>(action, initialState)- The action signature is
(prevState: State, formData: FormData) => Promise<State>
How is the use() function typed in TypeScript?
use<T>(resource: Promise<T> | React.Context<T>): T- The resolved type is inferred from the promise or context type
- It is imported from
"react":import { use } from "react"
What does the React Compiler do and do I need to change my code?
- The compiler (formerly React Forget) automatically memoizes components and hooks at build time
- It eliminates the need for manual
useMemo,useCallback, andReact.memoin most cases - No code changes are required -- existing manual memoization is compatible
What is the difference between use() and useContext()?
use()can be called conditionally (insideifblocks, loops, early returns) whileuseContextcannotuse()also accepts promises, not just context objectsuse(context)is the preferred replacement foruseContext(context)going forward
How should I approach migrating a large React 18 app to React 19?
- Incremental adoption is recommended: add
"use client"to existing components and adopt features file-by-file - Install React 19 types (
@types/reactand@types/react-domversion 19+) together - Replace
useFormStateimports withuseActionStatefrom"react" - Replace
<Context.Provider>with<Context value={v}>(optional, old syntax still works)
Related
- Server Components -- RSC model and serialization
- Server Actions -- Server functions and form integration
- Form Actions --
<form action={}>, useActionState, useFormStatus - use() Hook -- Reading promises and context
- useOptimistic -- Optimistic update patterns
- React Compiler -- Auto-memoization