22.7196°N — 75.8577°E / IST
← WRITING

B·04 / ENGINEERING

React 19 Features That Actually Change How You Write Code

A practical tour of React 19's biggest features — useActionState, the use API, useOptimistic, ref as a prop, document metadata, and better error reporting.

SEP 07, 2026 · 3 MIN READ

EACT · REACT19 · JAVASCRIPT · FRONTEND

React 19 has quietly reshaped how we write everyday UI code. A lot of patterns that used to need extra libraries or boilerplate are now just... part of React. Here's a practical tour of the features that actually change how you write code day to day.

1. Actions and the useActionState hook

Before React 19, handling a form submission meant wiring up useState for pending/error states, calling preventDefault, and manually managing the async flow. useActionState collapses all of that into one hook.

JAVASCRIPT
1import { useActionState } from "react";
2
3async function updateName(prevState, formData) {
4  const name = formData.get("name");
5  if (!name) {
6    return { error: "Name is required" };
7  }
8  await saveName(name);
9  return { error: null };
10}
11
12function NameForm() {
13  const [state, formAction, isPending] = useActionState(updateName, { error: null });
14
15  return (
16    <form action={formAction}>
17      <input type="text" name="name" />
18      <button type="submit" disabled={isPending}>
19        {isPending ? "Saving..." : "Save"}
20      </button>
21      {state.error && <p>{state.error}</p>}
22    </form>
23  );
24}

No more manual useState juggling for pending states. React tracks it for you, and the form still works even before hydration finishes.

2. The use API

use lets you read a promise or context value directly inside render, including conditionally — something hooks never allowed before.

JAVASCRIPT
1import { use, Suspense } from "react";
2
3function Comments({ commentsPromise }) {
4  const comments = use(commentsPromise);
5  return (
6    <ul>
7      {comments.map((c) => (
8        <li key={c.id}>{c.text}</li>
9      ))}
10    </ul>
11  );
12}
13
14function Page({ commentsPromise }) {
15  return (
16    <Suspense fallback={<p>Loading comments...</p>}>
17      <Comments commentsPromise={commentsPromise} />
18    </Suspense>
19  );
20}

The component just suspends until the promise resolves. No useEffect, no manual loading state.

3. useOptimistic for instant UI feedback

Optimistic updates used to mean hand-rolling a temporary state and rolling it back on failure. useOptimistic makes this a one-liner pattern.

JAVASCRIPT
1import { useOptimistic } from "react";
2
3function TodoList({ todos, addTodo }) {
4  const [optimisticTodos, addOptimisticTodo] = useOptimistic(
5    todos,
6    (state, newTodo) => [...state, { ...newTodo, sending: true }]
7  );
8
9  async function formAction(formData) {
10    const text = formData.get("text");
11    addOptimisticTodo({ id: crypto.randomUUID(), text });
12    await addTodo(text);
13  }
14
15  return (
16    <form action={formAction}>
17      <ul>
18        {optimisticTodos.map((todo) => (
19          <li key={todo.id} style={{ opacity: todo.sending ? 0.5 : 1 }}>
20            {todo.text}
21          </li>
22        ))}
23      </ul>
24      <input type="text" name="text" />
25      <button type="submit">Add</button>
26    </form>
27  );
28}

The UI updates instantly, then reconciles with the real server response once it arrives.

4. No more forwardRef

ref is now just a regular prop on function components. The wrapper that everyone loved to forget is gone.

JSX
1function Input({ label, ref }) {
2  return (
3    <label>
4      {label}
5      <input ref={ref} />
6    </label>
7  );
8}
9
10// usage
11const inputRef = useRef(null);
12<Input label="Email" ref={inputRef} />;

5. Document metadata as JSX

You can now render <title>, <meta>, and <link> tags directly from any component, even deep in the tree, and React hoists them into <head> automatically.

JSX
1function BlogPost({ post }) {
2  return (
3    <article>
4      <title>{post.title} — My Blog</title>
5      <meta name="description" content={post.description} />
6      <h1>{post.title}</h1>
7      <p>{post.body}</p>
8    </article>
9  );
10}

No more react-helmet for simple cases — it's built in.

6. Better error reporting

React 19 no longer double-logs errors caught by error boundaries, and adds onCaughtError / onUncaughtError root options for centralized error reporting instead of console-only visibility.

JAVASCRIPT
1import { createRoot } from "react-dom/client";
2
3const root = createRoot(document.getElementById("root"), {
4  onCaughtError: (error, errorInfo) => {
5    logErrorToService(error, errorInfo.componentStack);
6  },
7});

Wrapping up

React 19's theme is clear: less boilerplate for the async, form-heavy, optimistic-UI patterns that make up most real apps. useActionState, use, and useOptimistic alone remove a huge amount of custom state-management code that used to live in every project. If you've been holding off on upgrading, these are the features that make it worth doing sooner rather than later.