React 19 released on December 5, 2024 and is the most significant major version since hooks in React 16.8. The release graduates Server Components and Server Actions from experimental to stable, introduces three new hooks, and simplifies several long-standing patterns.
>use() — read promises and context anywhere
use() is a new hook that suspends a component until a promise resolves, or reads a context value. Unlike useContext, it can be called conditionally.
import { use, Suspense } from 'react';
// Read a promise — component suspends until resolved
function UserProfile({ userPromise }: { userPromise: Promise<User> }) {
const user = use(userPromise); // throws the promise → Suspense catches it
return <h1>{user.name}</h1>;
}
// Read context conditionally (unlike useContext)
function ThemedButton({ primary }: { primary: boolean }) {
if (primary) {
const theme = use(ThemeContext); // ✓ conditional call is allowed
return <button style={{ background: theme.primary }}>Click</button>;
}
return <button>Click</button>;
}
// Usage with Suspense
<Suspense fallback={<Spinner />}>
<UserProfile userPromise={fetchUser(id)} />
</Suspense>>Server Actions — stable
Server Actions are now stable. They are async functions marked with "use server" that run on the server and can be called directly from client components — no API route needed.
// actions.ts
'use server';
import { revalidatePath } from 'next/cache';
import { db } from '@/lib/db';
export async function createPost(formData: FormData) {
const title = formData.get('title') as string;
await db.post.create({ data: { title } });
revalidatePath('/posts');
}
// Client component — call the action directly
'use client';
import { createPost } from './actions';
export function NewPostForm() {
return (
<form action={createPost}>
<input name="title" placeholder="Post title" />
<button type="submit">Create</button>
</form>
);
}>useOptimistic — instant UI feedback
useOptimistic lets you show an optimistic UI state while a Server Action or async operation is in progress, then automatically reverts on error.
'use client';
import { useOptimistic, useTransition } from 'react';
import { toggleLike } from './actions';
function LikeButton({ postId, liked, count }: LikeButtonProps) {
const [optimisticLiked, setOptimisticLiked] = useOptimistic(liked);
const [optimisticCount, setOptimisticCount] = useOptimistic(count);
const [isPending, startTransition] = useTransition();
const handleClick = () => {
startTransition(async () => {
setOptimisticLiked(!liked);
setOptimisticCount(liked ? count - 1 : count + 1);
await toggleLike(postId); // actual server call
});
};
return (
<button onClick={handleClick} disabled={isPending}>
{optimisticLiked ? '❤️' : '🤍'} {optimisticCount}
</button>
);
}>ref as a prop — no more forwardRef
⚠ BREAKINGforwardRef is deprecated in React 19. Pass ref as a regular prop instead.
// ✗ React 18 — forwardRef required
const Input = forwardRef<HTMLInputElement, InputProps>((props, ref) => (
<input {...props} ref={ref} />
));
// ✓ React 19 — ref is a regular prop
function Input({ ref, ...props }: InputProps & { ref?: React.Ref<HTMLInputElement> }) {
return <input {...props} ref={ref} />;
}
// Usage unchanged
const inputRef = useRef<HTMLInputElement>(null);
<Input ref={inputRef} placeholder="Search..." />>Context as a provider
const ThemeContext = createContext<Theme>({ mode: 'light' });
// ✗ React 18
<ThemeContext.Provider value={{ mode: 'dark' }}>
{children}
</ThemeContext.Provider>
// ✓ React 19 — Context itself is the provider
<ThemeContext value={{ mode: 'dark' }}>
{children}
</ThemeContext>>New Document Metadata API
// React 19 natively hoists <title>, <meta>, <link> to <head>
// No need for react-helmet or next/head in pure React apps
function BlogPost({ post }: { post: Post }) {
return (
<>
<title>{post.title}</title>
<meta name="description" content={post.excerpt} />
<link rel="canonical" href={`https://example.com/posts/${post.slug}`} />
<article>{post.content}</article>
</>
);
}>Upgrade from React 18
npm install react@19 react-dom@19
# Automated codemod handles most changes
npx codemod@latest react/19/migration-recipe
# What the codemod handles:
# ✓ forwardRef → ref prop
# ✓ Context.Provider → Context as provider
# ✓ ReactDOM.render → createRoot (if not already done)
# ✓ Deprecated string refs removed
# Run your test suite — React 19 has strict mode improvements
# that may surface latent double-render issues