I've moved several side projects — and this site — to Next.js 16 with React 19 and Tailwind CSS 4. Most of the upgrade is smooth. These are the things that cost me time.
params is a Promise
In pages, layouts, route handlers and generateMetadata, params and searchParams are Promises. Forget the await and you get a confusing runtime error, not a type error, if you typed the props loosely.
export default async function Page({ params }: PageProps<"/blog/[slug]">) {
const { slug } = await params;
// ...
}Use the global PageProps and LayoutProps
Next generates route-aware types during next dev, next build or next typegen. PageProps<"/blog/[slug]"> gives strict keys for params — no hand-written interfaces, and a typo in the route literal is a compile error. LayoutProps<"/"> does the same for layouts.
middleware.ts is now proxy.ts
The middleware file convention is deprecated and renamed to proxy. Same idea — run code before a request is rendered for redirects, rewrites and headers — but the name now says what it is for. Don't build auth that depends on shared modules or globals there; it may run separately from your render code.
The docs ship with the package
node_modules/next/dist/docs/ contains the documentation for the exact version you installed. When blog posts and AI suggestions disagree, that folder wins. I grep it before touching any API I haven't used in this version.
Static by default, on purpose
This blog has no database. Posts are Markdown files read at build time:
export const dynamicParams = false;
export function generateStaticParams() {
return getAllPosts().map((p) => ({ slug: p.slug }));
}With dynamicParams = false, any slug not generated at build time is a clean 404 — no accidental server rendering of junk URLs. The RSS feed is a route handler marked force-static.
Tailwind 4 gotcha: @apply your own classes
In Tailwind 4 you can't @apply a plain CSS class you defined yourself. Declare it with @utility instead and it composes like any built-in:
@utility btn { @apply inline-flex items-center rounded-full px-5 py-2.5; }
@utility btn-primary { @apply btn bg-fg text-bg; }Verdict
Next 16 rewards you for being explicit: typed routes, static generation you opt out of, and file names that describe behaviour. Read the bundled docs first and the upgrade is mostly mechanical.
If the upgrade is also a chance to look at speed, the levers that matter are in Next.js performance.