React 19: Actions, use(), useOptimistic, and What Actually Changed
React 19 is a foundational shift in how you handle async mutations, data loading, and optimistic UI - here is every new API with real code examples.
Mahmudul Haque Qudrati
CEO & ML Engineer
One AI engineering post, weekly
LLM benchmarks, prompt techniques, and token-cost breakdowns — not another AI news roundup.
React 19 is the first major version since React 18 and it rewires how you think about async state. Instead of wiring up useEffect + useState + error boundaries manually, React 19 gives you first-class primitives for the entire async lifecycle.
React Actions
Actions are async functions that React knows about. When you pass them to <form action={fn}> or use them with the new hooks, React handles pending state, errors, and optimistic updates automatically.
import { useActionState } from "react";
async function submitForm(prevState: State, formData: FormData) {
const result = await saveToServer(formData.get("name") as string);
if (!result.ok) return { error: "Save failed" };
return { success: true };
}
function Form() {
const [state, action, isPending] = useActionState(submitForm, null);
return (
<form action={action}>
<input name="name" disabled={isPending} />
{state?.error && <p>{state.error}</p>}
<button type="submit" disabled={isPending}>
{isPending ? "Saving..." : "Save"}
</button>
</form>
);
}
Team workspace
Ship faster with chat, meetings, and projects in one place — Zlyqor.
The use() Hook
use() is the escape hatch that lets you read a Promise or Context inside the render function. Unlike other hooks, it can be called inside loops and conditionals.
import { use, Suspense } from "react";
function UserCard({ userPromise }: { userPromise: Promise<User> }) {
const user = use(userPromise); // suspends while pending
return <div>{user.name}</div>;
}
// In the parent:
<Suspense fallback={<Skeleton />}>
<UserCard userPromise={fetchUser(id)} />
</Suspense>
Reading context with use() is useful inside conditional branches:
if (needsTheme) {
const theme = use(ThemeContext); // valid - hooks rules relaxed for use()
}
useOptimistic()
Show the result of a mutation instantly while the server request is in flight:
import { useOptimistic, useTransition } from "react";
function TaskList({ tasks }: { tasks: Task[] }) {
const [optimisticTasks, addOptimistic] = useOptimistic(
tasks,
(state, newTask: Task) => [...state, newTask]
);
const [, startTransition] = useTransition();
function handleAdd(title: string) {
const newTask = { id: crypto.randomUUID(), title };
startTransition(async () => {
addOptimistic(newTask); // UI updates immediately
await createTask(title); // server catches up
});
}
return <ul>{optimisticTasks.map(t => <li key={t.id}>{t.title}</li>)}</ul>;
}
Refs as Props - No More forwardRef
In React 19, functional components accept ref as a regular prop. The forwardRef wrapper is no longer needed (and will be deprecated in a future minor):
// Before (React 18)
const Input = forwardRef<HTMLInputElement, Props>((props, ref) => (
<input {...props} ref={ref} />
));
// After (React 19)
function Input({ ref, ...props }: Props & { ref?: Ref<HTMLInputElement> }) {
return <input {...props} ref={ref} />;
}
Server Actions Are Stable
Server Actions work in both Server and Client Components. In a Client Component, import a Server Action and call it like a regular async function:
"use client";
import { deleteTask } from "@/app/actions";
function DeleteButton({ id }: { id: string }) {
return (
<button onClick={() => deleteTask(id)}>Delete</button>
);
}
The React Compiler (Formerly Forget)
The React Compiler - previously called React Forget - is in beta for React 19. It automatically memoizes components and values, eliminating the need for manual useMemo, useCallback, and memo() in most cases.
Enable it in Next.js 15:
// next.config.ts
export default {
experimental: {
reactCompiler: true,
},
};
Migration Steps
pnpm add react@19 react-dom@19- Replace
useFormStatewithuseActionState - Remove
forwardRef- passrefas a prop directly - Replace manual optimistic state patterns with
useOptimistic() - Run the React 19 codemod:
npx codemod@latest react/19/migration-recipe
References: React 19 blog · migration guide

Mahmudul Haque Qudrati
CEO & ML Engineer
Visionary technologist, software engineer, and machine learning specialist. Founder and CEO of Pristren, directing engineering teams that ship production-grade AI/ML pipelines, mission-critical full-stack applications, and developer tooling. Creator of Zlyqor, the unified team workspace platform. Author of 540+ technical guides and benchmark research reports on large language models, agentic workflows, Model Context Protocol (MCP), and modern web stacks.
More from Mahmudul
Related Articles
How to Build an MCP Server in TypeScript (Step-by-Step)
Build your first MCP server in TypeScript with the official SDK — tools, stdio transport, and a Claude Code test loop in under an hour.
Next.js App Router Patterns in 2026: What to Use and What to Avoid
App Router is the default in Next.js. Here are the patterns that work well in production and the ones that create more problems than they solve.
React Server Components: What They Are and When to Use Them
React Server Components run only on the server and never ship to the browser. Here is what that means in practice and when it actually helps.
// discussion
Comments