Next.js 14: server actions in practice
With Next.js 14, server actions are stable — and I can finally write mutations without separate API routes, right where they're needed.
A year ago I wrote about the App Router and called much of it exciting but unfinished. With Next.js 14, one piece of it has grown up: server actions are now stable. I've put them to work in real projects over the past weeks, and they change how I think about data mutations.
Mutations without the detour
Until now, every write operation went through an API route. A form sent its data to an endpoint, which validated, wrote to the database, and responded — and in the end I maintained two separate worlds: the front end and the API between them. Server actions close that gap. I write a function, mark it with "use server", and it's guaranteed to run on the server.
async function save(formData) {
"use server";
const name = formData.get("name");
await db.contact.create({ data: { name } });
revalidatePath("/contacts");
}
export default function Form() {
return (
<form action={save}>
<input name="name" />
<button type="submit">Save</button>
</form>
);
}
The form's action attribute takes the function directly. No fetch, no endpoint, no manual serializing. And the lovely part: it works even without JavaScript in the browser, because it builds on the web standard for forms. Progressive enhancement, without me working for it.
The best abstraction is the one that makes an entire layer unnecessary — not the one that adds another.
What holds up in practice
In real projects I value revalidatePath and revalidateTag most. After a mutation I tell Next.js precisely which data is stale, and the cache refreshes itself. No manual refetching, no orphaned state in the client.
Honestly, it took some discipline to draw the line cleanly. Server actions aren't a cure-all — for complex, multi-step flows or public interfaces I still reach for real endpoints. And validation belongs firmly on the server, because the client lies on principle.
But for what I build most often — forms, small mutations, the everyday life of an application — server actions feel right. They remove a layer I never loved. That's the kind of progress I like: less code that says more.