What's New in React 19: A Complete Guide

October 27, 2024 (1y ago)

React 19 brings a wave of exciting new features that make building interactive web applications easier and more intuitive. Let's dive into the most significant updates!

1. Actions: Simplifying Data Mutations

Actions are a game-changer for handling asynchronous operations. They automatically manage pending states and optimistic updates.

function AddToCartButton({ productId }) {
  const [isPending, startTransition] = useTransition();
 
  async function addToCart() {
    startTransition(async () => {
      await fetch("/api/cart", {
        method: "POST",
        body: JSON.stringify({ productId }),
      });
    });
  }
 
  return (
    <button onClick={addToCart} disabled={isPending}>
      {isPending ? "Adding..." : "Add to Cart"}
    </button>
  );
}

2. The use Hook: Await Promises in Components

The new use hook allows you to read resources like Promises and Context directly in your components.

import { use } from "react";
 
function UserProfile({ userPromise }) {
  // Suspend until the promise resolves
  const user = use(userPromise);
 
  return (
    <div>
      <h1>{user.name}</h1>
      <p>{user.email}</p>
    </div>
  );
}
 
// Usage with Suspense
function App() {
  const userPromise = fetchUser(userId);
 
  return (
    <Suspense fallback={<Loading />}>
      <UserProfile userPromise={userPromise} />
    </Suspense>
  );
}

3. useOptimistic: Instant UI Updates

Show optimistic updates immediately while waiting for server responses.

import { useOptimistic } from "react";
 
function TodoList({ todos }) {
  const [optimisticTodos, addOptimisticTodo] = useOptimistic(
    todos,
    (state, newTodo) => [...state, { ...newTodo, pending: true }]
  );
 
  async function createTodo(formData) {
    const newTodo = { id: Date.now(), text: formData.get("text") };
 
    // Show immediately in UI
    addOptimisticTodo(newTodo);
 
    // Send to server
    await fetch("/api/todos", {
      method: "POST",
      body: JSON.stringify(newTodo),
    });
  }
 
  return (
    <form action={createTodo}>
      <input name="text" />
      <button type="submit">Add</button>
      <ul>
        {optimisticTodos.map((todo) => (
          <li key={todo.id} style={{ opacity: todo.pending ? 0.5 : 1 }}>
            {todo.text}
          </li>
        ))}
      </ul>
    </form>
  );
}

4. useFormStatus: Track Form Submissions

Get information about form submission status without prop drilling.

import { useFormStatus } from "react-dom";
 
function SubmitButton() {
  const { pending, data } = useFormStatus();
 
  return (
    <button type="submit" disabled={pending}>
      {pending ? "Submitting..." : "Submit"}
    </button>
  );
}
 
function ContactForm() {
  async function handleSubmit(formData) {
    await fetch("/api/contact", {
      method: "POST",
      body: formData,
    });
  }
 
  return (
    <form action={handleSubmit}>
      <input name="email" type="email" required />
      <textarea name="message" required />
      <SubmitButton />
    </form>
  );
}

5. useFormState: Enhanced Form Handling

Manage form state and validation with ease.

import { useFormState } from "react-dom";
 
async function updateProfile(prevState, formData) {
  const name = formData.get("name");
 
  if (!name || name.length < 2) {
    return { error: "Name must be at least 2 characters" };
  }
 
  await fetch("/api/profile", {
    method: "PUT",
    body: JSON.stringify({ name }),
  });
 
  return { success: true, message: "Profile updated!" };
}
 
function ProfileForm() {
  const [state, formAction] = useFormState(updateProfile, { error: null });
 
  return (
    <form action={formAction}>
      <input name="name" required />
      {state.error && <p style={{ color: "red" }}>{state.error}</p>}
      {state.success && <p style={{ color: "green" }}>{state.message}</p>}
      <button type="submit">Update Profile</button>
    </form>
  );
}

6. Document Metadata Support

Manage document title and meta tags directly in components!

function BlogPost({ post }) {
  return (
    <>
      <title>{post.title} - My Blog</title>
      <meta name="description" content={post.summary} />
      <meta property="og:title" content={post.title} />
 
      <article>
        <h1>{post.title}</h1>
        <p>{post.content}</p>
      </article>
    </>
  );
}

7. Asset Loading Improvements

Better control over stylesheet and script loading with new lifecycle hooks.

function App() {
  return (
    <>
      {/* Preload critical resources */}
      <link rel="preload" href="/fonts/main.woff2" as="font" />
 
      {/* Load stylesheets with priority */}
      <link rel="stylesheet" href="/styles/critical.css" precedence="high" />
 
      <YourComponent />
    </>
  );
}

8. Ref as a Prop

No more forwardRef! Pass refs directly as props.

// Before React 19
const Input = forwardRef((props, ref) => {
  return <input ref={ref} {...props} />;
});
 
// React 19
function Input({ ref, ...props }) {
  return <input ref={ref} {...props} />;
}
 
// Usage remains the same
function Form() {
  const inputRef = useRef();
  return <Input ref={inputRef} />;
}

9. Context as a Provider

Simplified Context API usage.

import { createContext } from 'react';
 
const ThemeContext = createContext('light');
 
// Before React 19
<ThemeContext.Provider value="dark">
  <App />
</ThemeContext.Provider>
 
// React 19
<ThemeContext value="dark">
  <App />
</ThemeContext>

10. Enhanced Error Handling

Better error reporting with onCaughtError and onUncaughtError.

import { createRoot } from "react-dom/client";
 
const root = createRoot(document.getElementById("root"), {
  onCaughtError: (error, errorInfo) => {
    console.error("Caught error:", error, errorInfo);
    // Log to error tracking service
  },
  onUncaughtError: (error, errorInfo) => {
    console.error("Uncaught error:", error, errorInfo);
    // Log critical errors
  },
});
 
root.render(<App />);

Key Takeaways

React 19 focuses on:

These features make React more powerful and easier to use, especially for building data-driven applications with complex user interactions.

Ready to upgrade? Check out the official React 19 upgrade guide to get started!

// Start experimenting with React 19 today!
import { use, useOptimistic, useFormStatus } from "react";
 
console.log("Welcome to React 19! 🚀");