Home
About
Blog
Skills
Projects
Contact
Home
About
Blog
Skills
Projects
Contact
Back to Matrix
Web Dev 6/1/2026 6 min read

Mastering Next.js 15: What's New

Mastering Next.js 15: What's New
#React#Next.js#Frontend

Next.js 15 is less about new surface area and more about the framework being honest with you. Caching is explicit, the dev server is genuinely fast, and the request APIs finally admit they are asynchronous.

Caching Is No Longer Implicit

The headline change is that fetch requests, GET route handlers, and client-side navigation are not cached by default anymore. In earlier versions a plain fetch was silently force-cached, which produced an entire genre of bug report: the dashboard shows yesterday's numbers and nobody can work out why. Now you opt in.

```js
const res = await fetch('[api.example.com](https://api.example.com/stats)', {
  cache: 'force-cache',
  next: { revalidate: 3600, tags: ['stats'] },
});

Tagging is the part worth adopting properly. Once a fetch carries a tag, any server action can invalidate exactly that data with revalidateTag('stats') instead of nuking a whole route segment.

Async Request APIs

cookies(), headers(), params, and searchParams are now async. The migration is mechanical but it touches a lot of files.

```js
export default async function Page({ params }) {
  const { slug } = await params;
  const store = await cookies();
  const theme = store.get('theme')?.value ?? 'dark';
  return <Article slug={slug} theme={theme} />;
}

The reason behind the change matters more than the syntax: it lets the framework begin rendering before the request is fully resolved, which is what makes partial prerendering possible.

Turbopack for Development

Turbopack is stable for next dev. On a large App Router codebase, cold start and hot updates drop from seconds to well under a second, and the improvement compounds because you stop context-switching while waiting. Enable it with next dev --turbo and keep webpack for production builds until your plugin chain is verified.

Server Actions and useActionState

Mutations no longer need a client-side fetch wrapper, and useActionState gives you pending state and errors without a state machine.

```js
'use client';
import { useActionState } from 'react';
import { subscribe } from './actions';

export function Form() { const [state, action, pending] = useActionState(subscribe, null); return ( <form action={action}> <input name='email' type='email' required /> <button disabled={pending}>{pending ? 'Saving' : 'Subscribe'}</button> {state?.error && <p role='alert'>{state.error}</p>} </form> ); } ```

Treat every action as a public HTTP endpoint. Validate input with a schema and re-check authorisation inside the action itself, because the client never enforces anything.

Upgrade Notes Worth Reading Twice

  • Audit your fetches first. Anything that relied on implicit caching will now hit your origin on every request. Your API bill notices before your users do.
  • Run the codemod, then read the diff. npx @next/codemod@canary upgrade latest handles most async API changes but not conditional logic around them.
  • Check your instrumentation. Some analytics and error-reporting packages patch internals that moved.

The practical takeaway: Next.js 15 asks you to state your caching intent instead of inheriting a default. That is more typing and far fewer 2am debugging sessions.

Enjoyed this article?

Share it with your network and join the conversation.