devnotes.

React 19: Actions, New Hooks & Server Components

React 19 was released in December 2024 and is the current major stable version. It focused heavily on simplifying data mutations, forms, and async data — areas that previously needed a lot of manual useState/useEffect boilerplate.

Actions — functions that handle async transitions for you

An Action is any async function passed to something like a <form>'s action prop, a transition, or useActionState. React automatically tracks its pending state, errors, and optimistic updates, so you no longer hand-wire loading flags for every mutation.

<form action={async (formData) => {
  const name = formData.get('name');
  await saveStudent(name);
}}>
  <input name="name" />
  <button type="submit">Save</button>
</form>

useActionState — form state without the boilerplate

import { useActionState } from 'react';

async function updateName(previousState, formData) {
  const name = formData.get('name');
  if (!name) return { error: 'Name is required' };
  await saveStudent(name);
  return { error: null };
}

function ProfileForm() {
  const [state, formAction, isPending] = useActionState(updateName, { error: null });

  return (
    <form action={formAction}>
      <input name="name" />
      {state.error && <p className="error">{state.error}</p>}
      <button disabled={isPending}>{isPending ? 'Saving...' : 'Save'}</button>
    </form>
  );
}

useFormStatus — read the parent <form>'s pending state

Useful for building a reusable submit button without prop drilling the pending flag:

import { useFormStatus } from 'react-dom';

function SubmitButton() {
  const { pending } = useFormStatus();
  return <button disabled={pending}>{pending ? 'Saving...' : 'Save'}</button>;
}

useOptimistic — instant UI feedback before the server confirms

import { useOptimistic } from 'react';

function CommentList({ comments, addComment }) {
  const [optimisticComments, addOptimisticComment] = useOptimistic(
    comments,
    (state, newComment) => [...state, { ...newComment, sending: true }]
  );

  async function formAction(formData) {
    const text = formData.get('comment');
    addOptimisticComment({ text });
    await addComment(text); // real request; UI already shows the comment
  }

  return (
    <>
      {optimisticComments.map((c, i) => (
        <p key={i} style={{ opacity: c.sending ? 0.5 : 1 }}>{c.text}</p>
      ))}
      <form action={formAction}><input name="comment" /></form>
    </>
  );
}

use() — read a promise or context conditionally

use() is a new primitive that can read the value of a Promise or a Context, and — unlike other hooks — it can be called conditionally or in loops.

import { use, Suspense } from 'react';

function StudentName({ studentPromise }) {
  const student = use(studentPromise); // suspends until resolved
  return <h2>{student.name}</h2>;
}

function Page({ studentPromise }) {
  return (
    <Suspense fallback={<p>Loading...</p>}>
      <StudentName studentPromise={studentPromise} />
    </Suspense>
  );
}

ref as a regular prop — no more forwardRef

// React 19: just declare ref as a normal prop
function Input({ ref, ...props }) {
  return <input ref={ref} {...props} />;
}

// old React 18 way (still works, but no longer necessary)
const Input = forwardRef((props, ref) => <input ref={ref} {...props} />);

Other React 19 highlights

  • Server Components became a stable feature for framework authors to build on (used by Next.js App Router).
  • Document metadata — <title>, <meta>, and <link> tags can now be rendered directly inside any component and React hoists them into <head> automatically.
  • Stylesheet & async script support — React manages loading order and de-duplication of <link rel="stylesheet"> and <script async> tags rendered from components.
  • Better hydration error messages — mismatches between server and client HTML now show a readable diff instead of a generic warning.
  • Custom Elements (Web Components) — improved, more complete support for passing props and listening to events on custom elements.
What's New in React 19 — Actions, useActionState, use(), Server Components | DevNotes