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:
- Better async handling with Actions and the
usehook - Improved forms with
useFormStatusanduseFormState - Optimistic UI updates with
useOptimistic - Simplified APIs by removing
forwardRefand simplifying Context - Better metadata management with built-in support for document tags
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! 🚀");