0
Loading Experience

https://www.youtube.com/embed/IAYhEkVtNuQ?showinfo=0
For years, React developers have managed a complex dance of useEffect, loading states, and API endpoints just to submit a simple form. We've built massive client-side bundles just to validate an email address.
React 19 isn't just an update; it's a paradigm shift. It moves the heavy lifting to the server and lets us delete half our client-side code. Today, we're going to look at how to build "Full-Stack React" applications that feel instantaneous and are incredibly simple to maintain.
useEffect for Data FetchingWe've all written this code a hundred times:
// The Old Way 💀
useEffect(() => {
setIsLoading(true);
fetch('/api/user')
.then(res => res.json())
.then(data => setUser(data))
.finally(() => setIsLoading(false));
}, []);
It works, but it causes "waterfalls" (where one component waits for another to finish loading). In React 19, we have the new use() hook. It allows us to unwrap promises directly in our render cycle, often integrating with Suspense automatically.
The biggest game-changer is Server Actions. Traditionally, if you wanted to handle a form submission, you had to:
<form>.onSubmit handler.e.preventDefault().fetch('/api/submit', ...).pages/api/submit.ts.In React 19, you just write a function.
// actions.ts (Server-side)
'use server'
export async function createPost(formData: FormData) {
const title = formData.get('title');
await db.post.create({ data: { title } });
}
And then call it directly in your button or form:
// PostForm.tsx (Client-side)
import { createPost } from './actions';
export default function PostForm() {
return (
<form action={createPost}>
<input name="title" />
<button type="submit">Create</button>
</form>
);
}
That's it. React automatically handles the network request, the serialization, and the closure scope. It feels like magic, but it's just standard web standards (FormData) powered by smart compiler transforms.
"But how do I show a spinner?" I hear you ask. React 19 introduces useActionState (formerly useFormState) and useFormStatus to handle this without you needing to create useState variables manually.
import { useFormStatus } from 'react-dom';
function SubmitButton() {
const { pending } = useFormStatus();
return (
<button disabled={pending}>
{pending ? 'Submitting...' : 'Save Post'}
</button>
);
}
React 19 forces us to unlearn bad habits. It pushes us towards a web that feels instantaneous and codebases that are significantly easier to read. If you haven't tried it yet, fire up a Next.js App Router project today and try to build a form without useEffect. You'll never want to go back.
Get the latest articles and project updates delivered straight to your inbox.