Engineering
Next.js 14: Upgrade Notes From the First Week
Next.js 14 shipped at Next.js Conf last Thursday, and because our builds run on the App Router already, we upgraded two production projects over the past week, including the studio site you are reading this on. Version notes from the trenches, for teams deciding whether to move.
The headline framing from Vercel was interesting for what it was not: no big new paradigm, no API churn. After eighteen months of App Router upheaval, 14 is largely a stabilization release, and honestly that is the feature. The upgrades took us under an hour each, with one caveat below.
Server Actions are the real story
The marquee change is Server Actions hitting stable, and it deserves the attention. Mutations have been the App Router's awkward corner: rendering went server-first while writes still meant hand-rolling API routes and client fetch plumbing. Actions close that loop. A form posts to a function that runs on the server, full stop, with progressive enhancement for free, meaning the form works before hydration or without JavaScript at all.
// app/newsletter/actions.ts
"use server";
export async function subscribe(formData: FormData) {
const email = formData.get("email") as string;
await addSubscriber(email);
revalidatePath("/newsletter");
}
We converted our contact and newsletter forms as a trial and deleted a surprising amount of code: two API routes, a client-side fetch wrapper, loading-state management. The useFormStatus and useFormState hooks cover the pending and validation UX that used to justify a form library. There was some social media sneering last week about this looking like PHP. As someone who has maintained both eras: yes, and the ergonomics of "the function is just there" were always good. This version comes with type safety and a compiler.
Cautions from actual use: treat every action as a public endpoint, because it is one. Validate and authorize inside the action, not in the component that renders the form. And keep actions thin, delegating to the same service layer your other code uses, or you will smear business logic across your UI tree.
Turbopack, partial prerendering, and what we are waiting on
Turbopack is not default yet, but 14 declares it near-stable for dev, and our experience matches the pitch: local server startup dropped dramatically on our largest project, and hot refresh on deep routes feels roughly twice as fast. We have hit one plugin gap in an SVG loader, worked around in ten minutes. Verdict: turn it on locally with the --turbo flag, keep production builds on webpack, revisit quarterly.
Partial prerendering, previewed at the conference, is the genuinely exciting one: static shell served instantly from the edge, dynamic holes streamed in, no configuration. It is experimental and we are not shipping client work on it, but it points at a future where the static-versus-dynamic decision stops being route-level. That would dissolve one of the last architecture debates we have on every commerce build, the kind of thing we hit constantly on Northwind's product pages where a static page wants three dynamic regions.
Should you upgrade?
If you are on the App Router: yes, this week. It is a low-drama upgrade and Server Actions alone justify it. Note the housekeeping: minimum Node version moves to 18.17.
If you are still on the Pages Router: 14 changes nothing about the migration calculus, and pages remain fully supported. Our advice from July stands. Move on a greenfield project, not under a deadline.
Mostly, though, the release signals the App Router era entering its boring phase, and boring is what you want from infrastructure. The interesting problems are moving back up the stack, where they belong.
Building something this could apply to?
We take on a small number of flagship projects each quarter.
Start a project