search
HomeWeb Front-endJS TutorialNext.js v— Reflecting on Mistakes

Hello! This is another article about next.js. And finally, about the new version! Each release is a set of new, interesting, and controversial features. This version will be no exception. However, new version is interesting not so much for its new functionality, but for the change in priorities and organization in next.js. And yes, as you may have guessed from the title, a significant part of this release is valuable for reflecting on previous mistakes.

I've been working with next.js since around version 8. All this time I've been watching its development with interest (sometimes not without disappointment). Recently, I've published a series of articles about struggling with the new App Router - "Next.js App Router. A path to the future or a wrong turn", "Next.js caching. A gift or a curse", "More libraries for the god of libraries or how I rethought i18n". All of these were a result of very weak development of ideas and capabilities in previous versions of next.js. And because of this, my interest in the new version has only grown. Along with that, there's a desire to understand the vector of changes in the framework.

In this article, I won't dwell on what App Router or server components are - these are described in detail in previous articles. We'll focus only on the new version and only on the new changes.

Note: The article reflects the most interesting changes from the author's perspective. They differ from the official list, as the author selected them from commits and PRs in the framework.

Next.js v15 Release

First, a bit about changes in the internal development processes of next.js. For the first time, the framework team has published a release candidate (RC version). Obviously, they did this due to the React.js team's decision to publish React v19 RC.

Usually, the next.js team in their stable releases calmly uses react from the "Canary" release branch (this branch is considered stable and recommended for use by frameworks). This time, however, they decided to do things differently (spoiler - not in vain).

The plan for both teams was simple - publish a pre-release version, let the community check for issues, and in a couple of weeks publish a full release.

Next.js v— Reflecting on Mistakes


Tweet from React.js core-team developer https://x.com/acdlite/status/1797668537349328923

It's been over six months since the release candidate of React.js was released, but the stable version still hasn't been published. The delay in releasing the stable version of React.js has impacted next.js's plans as well. Therefore, contrary to tradition, they published a total of 15 additional patch versions while already working on the 15th version (usually 3-5 patches and then a release). What's noteworthy here is that these patch versions didn't include all accumulated changes, but only addressed critical issues, which also deviates from next.js's usual processes.

The basic release process in next.js is that everything merges into the canary branch, and then, at some point, this branch is published as a stable release.

However, as a result, the next.js team decided to decouple from the React.js release and publish a stable version of the framework before the stable version of React.js is released.

Documentation Versioning

Another very useful organizational change. Finally, it's possible to view different versions of the documentation. Here's why this is so important:

Firstly, updating next.js can often be quite a challenging task due to major changes. In fact, this is why there are still over 2 million downloads for version 12 and over 4 million for version 13 monthly (to be fair, version 14 has over 20 million downloads).

Consequently, users of previous versions need documentation specific to their version, as the new one might be rewritten for a half.

Next.js v— Reflecting on Mistakes


Next.js documentation versioning - nextjs.org/docs

Another problem is that Next.js essentially uses a single channel. Documentation changes are also made to it. Therefore, descriptions of changes from canary versions immediately appeared in the main documentation. Now they are displayed under the "canary" section.

Using React

At the beginning, I mentioned that Next.js is currently using the RC version of React.js. But in reality, this is not quite true, or rather not entirely true. In fact, Next.js is currently using two React.js configurations: the 19th canary version for App Router and the 18th version for Pages Router.

Interestingly, at one moment they wanted to include the 19th version for Pages Router as well, but then rolled back these changes. Now, full support for React.js version 19 is promised after the release of its stable version.

Along with this, the new version will have several useful improvements for server actions functions (yes, the React team renamed them):

  • Optimization of weight and performance;
  • Improved error handling;
  • Fixed revalidation and redirects from server functions.

I suppose I'll include Next.js's new feature in this section as well - the Form component. Overall, it's the familiar form from react-dom, but with some improvements. This component is primarily needed if successful form submission involves navigating to another page. For the next page, the loading.tsx and layout.tsx abstractions will be pre-loaded.

import Form from 'next/form'

export default function Page() {
  return (
    
; {/* On submission, the input value will be appended to the URL, e.g. /search?query=abc */} ; ;
; ) }

Developer Experience (DX)

When talking about Next.js, we can't ignore the developer experience. In addition to the standard "Faster, Higher, Stronger" (which we'll also discuss, but a bit later), several useful improvements have been released.

Long-awaited support for the latest ESLint. Next.js didn't support ESLint v9 until now. This is despite the fact that both eslint itself (v8) and some of its subdependencies are already marked as deprecated. This resulted in an unpleasant situation where projects were essentially forced to keep deprecated packages.

The error interface has been slightly improved (which in Next.js is already clear and convenient):

  • Added a button to copy the stack trace;
  • Added the ability to open the error source in the editor at a specific line.

Next.js v— Reflecting on Mistakes


Example of copying the error stack in next.js

A "Static Indicator" has been added - an element in the corner of the page showing that the page has been built in static mode.
Overall, it's a minor thing, but it's amusing that they included it in the key changes as something new. The indicator for a "pre-built" page has been around since roughly version 8 (2019) and here, essentially, they've just slightly updated it and adapted it for the App Router.

Next.js v— Reflecting on Mistakes

A directory with debugging information has also been added - .next/diagnostics. It will contain information about the build process and all errors that occur. It's not yet clear if this will be useful in daily use, but it will certainly be used when troubleshooting issues with Vercel devrels (yes, they sometimes help to solve problems).

Next.js v— Reflecting on Mistakes
Next.js team's response to a [tweet about slow project build](https://x.com/darshansrc/status/1797339543571755425)

Changes in the Build Process

After discussing DX, it's worth talking about the build process. And along with it, Turbopack.

Turbopack

And the biggest news in this area. Turbopack is now fully completed for development mode! "100% of existing tests passed without errors with Turbopack"

Now the Turbo team is working on the production version, gradually going through the tests and refining them (currently about 96% complete)

Next.js v— Reflecting on Mistakes
Example changelog section in next.js

Turbopack also adds new capabilities:

  • Setting a memory limit for builds with Turbopack;
  • Tree Shaking (removal of unused code).
import Form from 'next/form'

export default function Page() {
  return (
    
; {/* On submission, the input value will be appended to the URL, e.g. /search?query=abc */} ; ;
; ) }

These and other improvements in Turbopack "reduced memory usage by 25-30%" and also "accelerated the build of heavy pages by 30-50%".

Other

Significant style issues have been fixed. In version 14, situations often arose where the order of styles was broken during navigation, causing style A to become higher than style B, than vice versa. This changed their priority and consequently, elements looked different.

The next long-awaited improvement. Now the configuration file can be written in TypeScript - next.config.ts

const nextConfig = {
  experimental: {
    turbo: {
      treeShaking: true,
      memoryLimit: 1024 * 1024 * 512 // in bytes / 512MB
    },
  },
}

Another interesting update is retrying attempts for static page builds. This means if a page fails at build time (for example, due to internet problems) - it will try to build again.

import type { NextConfig } from 'next';

const nextConfig: NextConfig = {
  /* config options here */
};

export default nextConfig;

And to conclude this section, a functionality highly desired by the community - the ability to specify the path to additional files for building. With this option, you can, for example, specify that files are located not in the app directory, but in directories like modules/main, modules/invoices.

However, at the moment, they have only added it for internal team purposes. And in this version, they definitely won't present it. Going forward, it will either be used for Vercel needs, or they will test it and present it in the next release.

Changes in the Framework API

The most painful part of Next.js updates - API changes. And in this version, there are also breaking updates.

Several internal framework APIs have become asynchronous - cookies, headers, params and searchParams (so-called Dynamic APIs).

const nextConfig = {
  experimental: {
    staticGenerationRetryCount: 3,
  },
}

It's a major change, but the Next.js team promises that all this functionality can be updated automatically by calling their codemod:

npx @next/codemod@canary next-async-request-api .

Another change, but probably not relevant to many. The keys geo and ip have been removed from NextRequest (used in middleware and API routes). Essentially, this functionality only worked in Vercel, while in other places developers made their own methods. For Vercel, this functionality will be moved to the @vercel/functions package

And a few more updates:

  • In revalidateTag, you can now pass multiple tags at once;
  • Keys images.remotePatterns.search and images.localPatterns have been added to the configuration for next/image. These allow better control over address restrictions for image compression.
import Form from 'next/form'

export default function Page() {
  return (
    
; {/* On submission, the input value will be appended to the URL, e.g. /search?query=abc */} ; ;
; ) }

Caching

In my personal opinion, this is where the most important changes for Next.js have occurred. And the biggest news is - Caching is now disabled by default! I won't go into detail about caching problems, this was largely covered in the article "Next.js Caching. Gift or Curse".

Let's go through all the main changes in caching:

  • Specifically, fetch now uses the no-store value by default instead of force-cache;
  • API routes now work in force-dynamic mode by default (previously the default was force-static, meaning they were compiled into a static response during build time [if dynamic APIs were not used on the page]);
  • Caching in the client router has also been disabled. Previously, if a client visited a page within a route - it was cached on the client and remained in that state until the page was reloaded. Now, the current page will be loaded each time. This functionality can be reconfigured through next.config.js:
const nextConfig = {
  experimental: {
    turbo: {
      treeShaking: true,
      memoryLimit: 1024 * 1024 * 512 // in bytes / 512MB
    },
  },
}
  • Moreover, even if client-side caching is enabled - it will apparently be updated at the right moment. Specifically, if the cache of an enabled page on the server expires.
  • Server components are now cached in development mode. This makes updates in development happen faster. The cache can be cleared simply by reloading the page. You can also completely disable this functionality through next.config.js:
import type { NextConfig } from 'next';

const nextConfig: NextConfig = {
  /* config options here */
};

export default nextConfig;
  • You can now manage the "Cache-Control" header. Previously, it was always rigidly overwritten with Next.js's internal values. This caused artifacts with caching through CDN;
  • next/dynamic caches modules and reuses them, rather than loading them again each time;

That's regarding the "historical misunderstandings". New APIs will also appear in Next.js. Namely, the so-called Dynamic I/O. It hasn't been written about anywhere yet, so the following will be the author's guesses based on the changes.

Dynamic I/O appears to be an advanced mode of dynamic building. Something like PPR (Partial Prerendering), or more precisely, its complement. In short, Partial Prerendering is a page building mode where most elements are built at build time and cached, while individual elements are built for each request.

So, dynamic I/O [probably] finalizes the architecture for this logic. It expands caching capabilities so that it can be enabled and disabled precisely depending on the mode and place of use (whether in a "dynamic" block or not).

import Form from 'next/form'

export default function Page() {
  return (
    
; {/* On submission, the input value will be appended to the URL, e.g. /search?query=abc */} ; ;
; ) }

Along with this, the "use cache" directive is added. It will be available in nodejs and edge runtimes and, apparently, in all server segments and abstractions. By specifying this directive at the top of a function or a module exporting a function - its result will be cached. The directive will only be available when dynamicIO is enabled.

const nextConfig = {
  experimental: {
    turbo: {
      treeShaking: true,
      memoryLimit: 1024 * 1024 * 512 // in bytes / 512MB
    },
  },
}

Also, specifically for use cache, methods cacheLife and cacheTag are added

import type { NextConfig } from 'next';

const nextConfig: NextConfig = {
  /* config options here */
};

export default nextConfig;

cacheTag will be used for revalidation using revalidateTag, and cacheLife will set the cache lifetime. For the cacheLife value, you'll need to use one of the preset values. Several options will be available out of the box ("seconds", "minutes", "hours", "days", "weeks", "max"), additional ones can be specified in next.config.js:

const nextConfig = {
  experimental: {
    staticGenerationRetryCount: 3,
  },
}

Partial Prerendering (PPR)

Probably the main feature of the next release. As mentioned earlier, PPR is a page building mode where most elements are assembled at build time and cached, while individual elements are assembled for each request. At the same time, the pre-built part is immediately sent to the client, while the rest is loaded dynamically.

Next.js v— Reflecting on Mistakes


How Partial Prerendering works

The functionality itself was introduced six months ago in the release candidate as an experimental API. This API will remain in this state, and we will likely see it as stable only in version 16 (which is good, as major functionality often transitioned to stable within six months to a year).

Regarding the changes. As mentioned earlier, it primarily updated the working principles. However, from the perspective of using PPR, this hardly affected anything. At the same time, it received several improvements:

Previously, there was just a flag in the config, but now to enable PPR, you need to specify 'incremental'. This is apparently done to make the logic more transparent - content can be cached by developers even in PPR, and to update it, you need to call revalidate methods.

import { cookies } from 'next/headers';

export async function AdminPanel() {
  const cookieStore = await cookies();
  const token = cookieStore.get('token');

  // ...
}

Also, previously PPR was launched for the entire project, but now it needs to be enabled for each segment (layout or page):

const nextConfig = {
  images: {
    localPatterns: [
      {
        pathname: '/assets/images/**',
        search: 'v=1',
      },
    ],
  },
}

Another change is Partial Fallback Prerendering (PFPR). It's precisely due to this improvement that the pre-built part is immediately sent to the client, while the rest is loaded dynamically. In place of dynamic elements, a callback component is shown during this time.

import Form from 'next/form'

export default function Page() {
  return (
    
; {/* On submission, the input value will be appended to the URL, e.g. /search?query=abc */} ; ;
; ) }

Instrumentation

Instrumentation is marked as a stable API. The instrumentation file allows users to hook into the lifecycle of the Next.js server. It works across the entire application (including all segments of Pages Router and App Router).

Currently, instrumentation supports the following hooks:

register - called once when initializing the Next.js server. It can be used for integration with observability libraries (OpenTelemetry, datadog) or for project-specific tasks.

onRequestError - a new hook that is called for all server errors. It can be used for integrations with error tracking libraries (Sentry).

const nextConfig = {
  experimental: {
    turbo: {
      treeShaking: true,
      memoryLimit: 1024 * 1024 * 512 // in bytes / 512MB
    },
  },
}

Interceptor

Interceptor, also known as route-level middleware. It's something like a full-fledged [already existing] middleware, but unlike the latter:

  • It can work in the Node.js runtime;
  • It works on the server (which means it has access to the environment and unified cache);
  • It can be added multiple times and is inherited in nesting (similar to how middleware worked when it was in beta version);
  • It also works for server functions.

Moreover, when creating an interceptor file, all pages below in the tree become dynamic.

import type { NextConfig } from 'next';

const nextConfig: NextConfig = {
  /* config options here */
};

export default nextConfig;

Speaking of Vercel, middleware will now be effective as a primary simple check at the CDN level (thus, for example, immediately returning redirects if the request is not allowed), while interceptors will work on the server, performing full-fledged checks and complex operations.

In self-hosting, however, such a division will apparently be less effective (since both abstractions work on the server). It may be sufficient to use only interceptors.

Conclusions

Overwriting fetch, aggressive caching, numerous bugs, and ignoring community requests. The Next.js team made erroneous decisions, rushed releases, and held onto their views despite community feedback. It took almost a year to recognize the problems. And only now, finally, there's a sense that the framework is once again addressing community issues.

On the other hand, there are other frameworks. A year ago, at the React.js presentation, it seemed that all frameworks would soon be on par with Next.js. React started mentioning Next.js less frequently as the main tool, frameworks were showcasing upcoming build systems, support for server components and functions, and a series of global changes and integrations. Time has passed, and essentially, none of them have reached that point yet.

Of course, final conclusions can only be drawn after some time, but for now, it feels like the changes in React.js, instead of the expected leveling of frameworks, have led to even greater dominance of Next.js and a wider divergence between frameworks (since the implementation of server components and actions was left to the discretion of the frameworks).

At the same time, OpenAI switched to Remix ("due to its greater stability and convenience"):

Next.js v— Reflecting on Mistakes


Remix usage in ChatGPT

And apparently they started before significant changes in Next.js

Next.js v— Reflecting on Mistakes


Tweet about ChatGPT switching to Remix from August, 2024

In general, in the next stateofjs and stackoverflow surveys, we are likely to see significant reshuffling.

Credits

Code examples or their foundations are taken from next.js documentation, as well as from commits, PRs, and the next.js core;

Postscript

If you need a tool for generating documentation based on MD files - take a look at robindoc.com, if you work with next.js - you might find something useful in the solutions at nimpl.tech.

The above is the detailed content of Next.js v— Reflecting on Mistakes. For more information, please follow other related articles on the PHP Chinese website!

Statement
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn
Python vs. JavaScript: A Comparative Analysis for DevelopersPython vs. JavaScript: A Comparative Analysis for DevelopersMay 09, 2025 am 12:22 AM

The main difference between Python and JavaScript is the type system and application scenarios. 1. Python uses dynamic types, suitable for scientific computing and data analysis. 2. JavaScript adopts weak types and is widely used in front-end and full-stack development. The two have their own advantages in asynchronous programming and performance optimization, and should be decided according to project requirements when choosing.

Python vs. JavaScript: Choosing the Right Tool for the JobPython vs. JavaScript: Choosing the Right Tool for the JobMay 08, 2025 am 12:10 AM

Whether to choose Python or JavaScript depends on the project type: 1) Choose Python for data science and automation tasks; 2) Choose JavaScript for front-end and full-stack development. Python is favored for its powerful library in data processing and automation, while JavaScript is indispensable for its advantages in web interaction and full-stack development.

Python and JavaScript: Understanding the Strengths of EachPython and JavaScript: Understanding the Strengths of EachMay 06, 2025 am 12:15 AM

Python and JavaScript each have their own advantages, and the choice depends on project needs and personal preferences. 1. Python is easy to learn, with concise syntax, suitable for data science and back-end development, but has a slow execution speed. 2. JavaScript is everywhere in front-end development and has strong asynchronous programming capabilities. Node.js makes it suitable for full-stack development, but the syntax may be complex and error-prone.

JavaScript's Core: Is It Built on C or C  ?JavaScript's Core: Is It Built on C or C ?May 05, 2025 am 12:07 AM

JavaScriptisnotbuiltonCorC ;it'saninterpretedlanguagethatrunsonenginesoftenwritteninC .1)JavaScriptwasdesignedasalightweight,interpretedlanguageforwebbrowsers.2)EnginesevolvedfromsimpleinterpreterstoJITcompilers,typicallyinC ,improvingperformance.

JavaScript Applications: From Front-End to Back-EndJavaScript Applications: From Front-End to Back-EndMay 04, 2025 am 12:12 AM

JavaScript can be used for front-end and back-end development. The front-end enhances the user experience through DOM operations, and the back-end handles server tasks through Node.js. 1. Front-end example: Change the content of the web page text. 2. Backend example: Create a Node.js server.

Python vs. JavaScript: Which Language Should You Learn?Python vs. JavaScript: Which Language Should You Learn?May 03, 2025 am 12:10 AM

Choosing Python or JavaScript should be based on career development, learning curve and ecosystem: 1) Career development: Python is suitable for data science and back-end development, while JavaScript is suitable for front-end and full-stack development. 2) Learning curve: Python syntax is concise and suitable for beginners; JavaScript syntax is flexible. 3) Ecosystem: Python has rich scientific computing libraries, and JavaScript has a powerful front-end framework.

JavaScript Frameworks: Powering Modern Web DevelopmentJavaScript Frameworks: Powering Modern Web DevelopmentMay 02, 2025 am 12:04 AM

The power of the JavaScript framework lies in simplifying development, improving user experience and application performance. When choosing a framework, consider: 1. Project size and complexity, 2. Team experience, 3. Ecosystem and community support.

The Relationship Between JavaScript, C  , and BrowsersThe Relationship Between JavaScript, C , and BrowsersMay 01, 2025 am 12:06 AM

Introduction I know you may find it strange, what exactly does JavaScript, C and browser have to do? They seem to be unrelated, but in fact, they play a very important role in modern web development. Today we will discuss the close connection between these three. Through this article, you will learn how JavaScript runs in the browser, the role of C in the browser engine, and how they work together to drive rendering and interaction of web pages. We all know the relationship between JavaScript and browser. JavaScript is the core language of front-end development. It runs directly in the browser, making web pages vivid and interesting. Have you ever wondered why JavaScr

See all articles

Hot AI Tools

Undresser.AI Undress

Undresser.AI Undress

AI-powered app for creating realistic nude photos

AI Clothes Remover

AI Clothes Remover

Online AI tool for removing clothes from photos.

Undress AI Tool

Undress AI Tool

Undress images for free

Clothoff.io

Clothoff.io

AI clothes remover

Video Face Swap

Video Face Swap

Swap faces in any video effortlessly with our completely free AI face swap tool!

Hot Article

Hot Tools

SublimeText3 Mac version

SublimeText3 Mac version

God-level code editing software (SublimeText3)

Dreamweaver CS6

Dreamweaver CS6

Visual web development tools

WebStorm Mac version

WebStorm Mac version

Useful JavaScript development tools

PhpStorm Mac version

PhpStorm Mac version

The latest (2018.2.1) professional PHP integrated development tool

mPDF

mPDF

mPDF is a PHP library that can generate PDF files from UTF-8 encoded HTML. The original author, Ian Back, wrote mPDF to output PDF files "on the fly" from his website and handle different languages. It is slower than original scripts like HTML2FPDF and produces larger files when using Unicode fonts, but supports CSS styles etc. and has a lot of enhancements. Supports almost all languages, including RTL (Arabic and Hebrew) and CJK (Chinese, Japanese and Korean). Supports nested block-level elements (such as P, DIV),