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 at Pristren

Mahmudul Haque Qudrati

CEO & ML Engineer

March 12, 2026
7 min read
React 19: Actions, use(), useOptimistic, and What Actually Changed

What Is React 19?

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.

tsx
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.

Start free

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.

tsx
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:

tsx
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:

tsx
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):

snippet
// 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:

tsx
"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:

typescript
// next.config.ts
export default {
  experimental: {
    reactCompiler: true,
  },
};

Migration Steps

  1. pnpm add react@19 react-dom@19
  2. Replace useFormState with useActionState
  3. Remove forwardRef - pass ref as a prop directly
  4. Replace manual optimistic state patterns with useOptimistic()
  5. Run the React 19 codemod: npx codemod@latest react/19/migration-recipe

References: React 19 blog · migration guide

#react-19#actions#use()#useoptimistic#server-components

// discussion

Comments

0/4000
Mahmudul Haque Qudrati — CEO & ML Engineer at Pristren

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.

PristrenZlyqor