React 19 is the most significant React release in years, bringing server-first architecture, a new compilation model, and simplified APIs that fundamentally change how we build React applications. Whether you're starting fresh or migrating, this guide covers everything.
🚀 The Big Changes at a Glance
- React Compiler — Automatic memoisation, no more manual useMemo/useCallback
- Actions — First-class support for form submissions and mutations
- use() hook — Read promises and context anywhere, including conditionally
- Server Components (stable) — Components that run on the server by default
- Document Metadata — Native <title>, <meta>, <link> support in components
1. React Compiler — Automatic Memoisation
The React Compiler (formerly React Forget) analyses your code at build time and automatically inserts memoisation. This eliminates the need for useMemo, useCallback, and React.memo in most cases.
Before (React 18)
import { useMemo, useCallback } from 'react';
function ProductList({ products, onSelect }) {
const sorted = useMemo(
() => products.sort((a, b) => a.price - b.price),
[products]
);
const handleClick = useCallback(
(id) => onSelect(id),
[onSelect]
);
return sorted.map(p => (
<ProductCard key={p.id} product={p} onClick={handleClick} />
));
}
After (React 19)
// No manual memoisation needed — the compiler handles it!
function ProductList({ products, onSelect }) {
const sorted = products.sort((a, b) => a.price - b.price);
return sorted.map(p => (
<ProductCard key={p.id} product={p} onClick={() => onSelect(p.id)} />
));
}
2. Actions — Simplified Form Handling
Actions replace the old pattern of onSubmit + useState + try/catch for mutations. They work with both client and server functions.
function ContactForm() {
async function submitForm(formData) {
'use server';
const name = formData.get('name');
const email = formData.get('email');
await db.contacts.create({ name, email });
}
return (
<form action={submitForm}>
<input name="name" placeholder="Name" />
<input name="email" type="email" placeholder="Email" />
<button type="submit">Send</button>
</form>
);
}
3. The use() Hook
The use() hook reads async resources like promises and context — and unlike other hooks, it can be called inside conditionals and loops.
import { use, Suspense } from 'react';
function UserProfile({ userPromise }) {
const user = use(userPromise); // Suspends until resolved
return (
<div>
<h2>{user.name}</h2>
<p>{user.email}</p>
</div>
);
}
// Usage
<Suspense fallback={<Skeleton />}>
<UserProfile userPromise={fetchUser(id)} />
</Suspense>
4. Document Metadata in Components
You can now render <title>, <meta>, and <link> directly in your components — React hoists them to <head> automatically.
function BlogPost({ post }) {
return (
<article>
<title>{post.title} | My Blog</title>
<meta name="description" content={post.excerpt} />
<h1>{post.title}</h1>
<p>{post.content}</p>
</article>
);
}
Migration Tips
- Remove unnecessary
useMemoanduseCallbackcalls - Replace
forwardRef— refs are now passed as regular props - Update form handling to use Actions where possible
- Adopt
use()for data fetching with Suspense - Run the React 19 codemod:
npx @react-codemod/react-19