Next.js 14 to 16: what I'm weighing before upgrading this site
The site you're reading this on runs Next.js 14.1. Two major versions have shipped since. Every few months I open the upgrade guide, read it properly, and put it down again.
That is not procrastination — or not only. An upgrade is a cost you pay now against a benefit you collect later, and it is worth being honest about both sides before you spend an evening on it.
What actually pushes me toward it
Support windows are finite. Older majors stop getting security patches. For a personal site that is a small risk; for the client work I do it is not a risk I get to take. Staying two majors behind is a habit, and habits transfer.
Caching stopped being a puzzle. The thing that cost me the most time in the App Router era was never Server Components — it was working out which caching layer had decided to serve me something stale. The direction of travel since 14 has been toward caching you opt into rather than caching you fight. That is worth real money on a project with a content team.
The dev loop. Build and refresh times compound. A second saved per iteration is invisible on any given day and enormous across a quarter.
What holds me back
The blast radius is not the framework. It is the ecosystem around it. On this site that means Framer Motion, marked, highlight.js, EmailJS and Tailwind. The framework upgrade is documented and boring. The dependency that quietly assumed an older React is the one that eats the evening.
Nothing here is broken. This site builds clean, ships static, and loads fast. "Newer" is not a user-facing feature. If I could not name what improves for a visitor, that would be an argument for leaving it alone.
I own the deadline, which makes it dangerous. Work upgrades get planned, tested and reviewed. Personal-project upgrades get done at 23:00 and merged without a second pair of eyes.
The order I'd actually do it in
If you are sitting where I am, this is the sequence I'd follow — and it is deliberately not "run the codemod and see what happens":
- Pin and record. Lockfile committed, build output saved, Lighthouse numbers written down. You cannot tell whether an upgrade regressed anything without a before.
- Upgrade React first, on the current Next.js. Separating the two means that when something breaks you know which one to blame.
- One major at a time. 14 to 15, ship it, live with it. Then 15 to 16. Skipping the middle version saves an hour and costs you the ability to bisect.
- Dependencies before framework. Bump the ecosystem to versions that already support the target React, while you are still on ground that works.
- Read the codemod output, don't just run it. The automated fixes are good. They are also a list of every assumption the framework changed, which is the actual value.
- Compare against your recorded numbers. If nothing improved and nothing broke, that is still a result — write it down so the next upgrade decision starts from evidence.
What I tell clients
The question is never "are we on the latest version". It is "how far behind can we get before an upgrade stops being a task and becomes a project". Two majors is usually still a task. Four is usually a project. The cost curve is not linear, and the moment it bends is the moment you stopped being able to schedule it.
So: I will do this one. In that order, on a branch, with the numbers written down first. Just not at 23:00.