Next.js Latest features

 Reynaldo Rodríguez
Reynaldo Rodríguez
February 7, 2024
Nodejs
React
Software Development
Next.js Latest features

Next.js new features


Next.js, the leading framework for building React applications, has recently introduced groundbreaking features in its versions 13 and 14. These updates, leveraging the latest React advancements, have transformed the way developers can build fast, efficient, and interactive web applications. This blog post aims to provide an in-depth understanding of these new features: App Router, React Server Components, Streaming, and Server Actions. We will also guide you through a practical application using these features, integrating with the TMDB API for real-world use cases.


Traditional Page Router in Next.js

In earlier versions of Next.js, routing was primarily handled through the Page Router. This system was based on the file system in the pages directory. Each file in this directory corresponded to a route in your application. For example, creating a file named about.js in the pages directory would automatically create a route /about.

Key Characteristics of Page Router:

  • File-Based Routing: Routes are automatically set up based on the file structure.
  • Static and Dynamic Routes: Supports both static routes (like /about) and dynamic routes (using file names like [id].js for variable paths like /posts/123).
  • Server-Side and Static Generation: Pages could be pre-rendered statically or on the server-side using functions like getStaticProps or getServerSideProps.
  • Client-Side Navigation: Supports client-side navigation using the Link component from next/link, enabling faster page transitions without a full page reload.

Next.js New App Router

The new App Router introduced in Next.js is a significant evolution of this model. It's designed to offer more flexibility, control, and efficiency in routing. The App Router works in a new directory named app. The app directory works alongside the pages directory to allow for incremental adoption. This allows you to opt some routes of your application into the new behavior while keeping other routes in the pages directory for previous behavior. By default, components inside app are React Server Components. This is a performance optimization and allows you to easily adopt them, and you can also use Client Components.

Key Features of App Router:

  • Dynamic Route Handling: The App Router provides enhanced control over routing, allowing for more dynamic handling of routes and their corresponding data fetching and rendering logic.
  • Integrated Data Fetching: It enables a more integrated approach to data fetching that's closely tied to the route itself, offering a more efficient way to load and render data-dependent components.
  • Enhanced Code Splitting: Improved code splitting capabilities ensure that only the necessary code for a particular route is loaded, enhancing application performance.
  • Streamlined Page Transitions: The App Router facilitates smoother and more seamless page transitions, improving the user experience.
  • Layouts and Nested Routes: It supports complex layouts and nested routes more naturally, allowing developers to structure their applications more effectively. Also, layouts do not rerender and can be cached, avoiding unnecessary computation when navigating between pages, which is a key performance optimization​​.
  • Caching logic: Next.js implements a multi-layered caching strategy to optimize both server and client performance. The App Router in Next.js allows for efficient caching and reuse of layouts, as mentioned before. Caching in Next.js extends to various other aspects, such as data fetching and server actions through the extended fetch API which includes request memoization, data caching, and revalidation.
  • TypeScript-first development experience: comes with improved support for TypeScript, offering enhanced type safety and a better DX.
  • Better Integration with React Features: The App Router is designed to work seamlessly with the latest React features, like Server Components and Suspense, unlocking new patterns for data fetching and component rendering.

How Does the App Router Differ From the Page Router?

  • Flexibility in Routing: While the Page Router was tied to the file system, the App Router provides more flexibility in defining routes, especially for complex applications.
  • Integrated Data Fetching: The App Router offers a more seamless integration between routing and data fetching, reducing the need for separate data fetching logic scattered across components.
  • Enhanced Performance: With better code splitting and integrated data fetching, the App Router can lead to performance improvements, especially for large and complex applications.
  • Advanced Patterns: The new router supports advanced patterns enabled by the latest React features, which were not as straightforward to implement with the Page Router.

The introduction of the App Router in Next.js represents a significant step forward in how developers can build and structure their applications. It provides enhanced flexibility, control, and efficiency, particularly for complex applications that require dynamic routing and integrated data fetching. The traditional Page Router, while effective for many use cases, is more static and less integrated with advanced React features compared to the App Router.

The App Router's ability to seamlessly integrate with React's latest features, like Server Components and Suspense, allows developers to build more efficient and responsive applications. This modern approach to routing reflects the evolving needs of web applications, where performance, user experience, and developer ergonomics are paramount.

In summary, the new App Router in Next.js is a more dynamic, efficient, and flexible routing solution compared to the traditional Page Router. It's designed to cater to the needs of modern web applications, enabling developers to build faster and more interactive user experiences with less effort and more control over the routing logic.

Traditional Client-Side Components in Next.js

Client-side components in Next.js and React are what developers traditionally use to build their applications. These components run in the browser and form the backbone of the interactive user interface of a React application.

Characteristics of Client-Side Components:

  • Rendering: They are rendered in the browser, meaning all the necessary code for these components is sent to the client.
  • Interactivity: Client-side components handle user interactions, state management, and effects (like data fetching after component mounts).
  • Bundle Size: The JavaScript bundle sent to the client includes all the code for these components, which can become large for complex applications.
  • Dynamic: They can change and update in response to user interactions without needing to go back to the server.

Next.js Server Components

Server Components, introduced in Next.js with React's latest updates, represent a significant shift in how components can be rendered and delivered to the client.

Key Features of Server Components:

  • Server-Side Rendering: These components are rendered on the server. Only the output (HTML and minimal JavaScript) is sent to the client.
  • Reduced JavaScript Load: Since the bulk of the component logic is executed on the server, the JavaScript bundle size sent to the client is significantly reduced.
  • Direct Access to Server-Side Resources: Server Components can directly access server-side resources (like databases or file systems), which client-side components cannot.
  • No Client-Side Interactivity: These components do not have interactive features like state or effects; they're more suited for parts of the app that are static or don't require client-side interactivity.

Differences Between Server Components and Client Components

  • Rendering Location: The most significant difference is where the rendering takes place. Server Components are rendered on the server, while client components are rendered in the browser.
  • Bundle Size Impact: Server Components help reduce the JavaScript bundle size, enhancing performance, especially for users on slower networks or devices. Client components, on the other hand, contribute to the bundle size.
  • Interactivity: Client-side components are dynamic and interactive, capable of handling state and user interactions. Server Components, being static, do not handle interactivity; they are more like templates that are filled out on the server.
  • Access to Server-Side Capabilities: Server Components have the advantage of directly accessing server-side features like databases, which is not possible with client-side components.
  • Use Cases: Server Components are ideal for sections of your application that are static, don't require direct user interaction, or need to access server-side resources efficiently. Client components are used for parts of the application that require interactivity and dynamic content.

Next.js's Server Components offer a powerful new way to build applications, allowing developers to write components that leverage the server's capabilities for rendering and data access. This approach can lead to significantly improved performance due to reduced JavaScript payloads. It's important to note that Server Components complement, rather than replace, client-side components. They are used together to build efficient, fast, and interactive web applications, each serving different parts of the application based on their strengths.

In essence, Server Components allow for a more nuanced approach to building web applications. They enable developers to offload non-interactive, server-intensive tasks to the server, leading to a leaner, more efficient client-side experience. This is particularly beneficial for content-heavy, data-intensive sections of an application where client-side rendering would be less efficient.

On the other hand, client-side components continue to be essential for building interactive and dynamic user interfaces. They are crucial for handling user inputs, client-side state management, and providing an engaging user experience.

By combining Server Components and client-side components, Next.js offers a comprehensive solution that optimizes both the performance and interactivity of web applications. This hybrid approach aligns with modern web development practices, where performance, scalability, and user experience are key.

To summarize, the introduction of Server Components in Next.js represents a significant advancement in optimizing web applications for performance and scalability, while client-side components remain indispensable for creating interactive and dynamic user interfaces. The choice between server and client components in a Next.js application should be guided by the specific needs of the application, such as interactivity, data requirements, and performance goals.

Traditional Content Delivery in Web Applications

Before the advent of Streaming in web development, web pages were typically delivered to the browser in a more static manner. Here's how it generally worked:

  • Whole Page Loading: The entire HTML content of a page had to be generated and sent to the client in one go. This process often meant that users had to wait longer to see any content, especially for complex pages with a lot of data.
  • Client-Side Rendering Delay: In single-page applications (SPAs) using frameworks like React, the browser first loads a minimal HTML page, then fetches JavaScript, which in turn fetches data and renders the content client-side. This can lead to a delay before the user sees the full content.
  • Initial Load Performance: The initial load performance could be sluggish, particularly for content-heavy sites or applications, as the browser needs to wait for the entire page's content to be ready before displaying anything to the user.

Caching in Next.js

Next.js 14 emphasizes extensive caching to enhance performance and minimize costs, adopting an approach where routes and data requests are cached by default at build time and upon their first visit unless you opt out. This cache mechanisms can be separated in:

Server-Side Caching:

  • Full Route Cache: The server caches the full output of a route. Particularly beneficial for static routes, this cache delivers content quickly without re-rendering for each request.
  • Data Cache: Next.js caches server-side data requests.This caching mechanism stores results from data fetch operations, reducing repeated database queries or API calls.

Client-Side Caching:

  • Router Cache: This client-side cache in Next.js 14 helps optimize navigation between routes. When a user navigates to a route that has been visited before, Next.js can quickly render the page from this cache, resulting in a much faster user experience. This cache is particularly effective for SPA-like experiences where users often go back and forth between a few key routes.

Enhanced Fetch API

Next.js 14 wraps the standard fetch API to extend its capabilities for caching and tagging, introducing significant enhancements in data fetching and caching mechanisms.

Deduplication of Requests:

  • Next.js 14, leveraging React's advancements, extends fetch to automatically memoize fetch requests during the rendering of a React component tree. This deduplication means that if multiple components request the same data during a single render pass, only one network request is made, and the response is shared among all requesting components.

Using Tags for Revalidation:

  • Next.js introduces a nuanced approach to revalidating cached data using tags. This method involves tagging cached data and providing functionality to selectively revalidate cached items based on these tags.
  • The revalidateTag method is a key feature in this approach. It allows developers to revalidate all cached data associated with a specific tag. This is particularly useful when data changes are made that affect multiple parts of the application, ensuring that all relevant cached data is updated promptly.
  • Additionally, the revalidatePath method offers a way to revalidate data for specific routes. This is useful for scenarios where data changes are localized to particular parts of the application, allowing for targeted and efficient data revalidation.

All these features come thanks to the fetch wrapper, but what if your applications are using third-party libraries that do not support or expose the fetch API (like database, CMS, or ORM clients)? Next.js offers a powerful alternative. Developers can configure the caching and revalidating behavior of these requests using a mix of Route Segment Config Option (for route caching), React's cache function (for deduplication of requests) and the unstable_cache API (for caching and revalidating request responses). This allows for a consistent and optimized data fetching strategy across different data sources.

In conclusion, these enhancements in the fetch API by Next.js 14 represent a significant leap in handling data fetching, caching, and revalidation. They provide developers with more control and flexibility in managing data, ensuring efficient data usage, reducing redundant network requests, and maintaining data integrity throughout the application. This approach not only improves performance but also contributes to a more responsive and up-to-date user experience.

Next.js Streaming

Next.js introduced the concept of Streaming as part of its new features, leveraging React 18's capabilities for streaming server-side rendering (SSR). This approach fundamentally changes how content is delivered and rendered.

Key Features of Next.js Streaming:

  • Incremental Rendering: Instead of waiting for the entire page to be ready, Streaming allows chunks of content to be sent to the browser as soon as they are ready. This means that the user can start seeing parts of the page much faster.
  • Improved Perceived Performance: Even if the total load time remains the same, the perceived performance is much better because users see content sooner. This incremental loading can significantly enhance the user experience, especially on slower networks.
  • Server-Side Generation: Streaming is particularly powerful in conjunction with server-side rendering. The server can start sending HTML as it's generated, rather than waiting for the entire page to be rendered.
  • React Suspense Integration: With the integration of React Suspense, Streaming in Next.js allows for finer control over the loading state of different components, enabling more dynamic and responsive user interfaces.

How Does Streaming Differ From Non-Streaming Approaches?

  • Perceived Load Time: The most significant difference is in how quickly content appears to the user. Streaming reduces the time to first byte (TTFB) and time to first meaningful paint (TFMP), improving the user experience.
  • Resource Utilization: Streaming allows the browser to start processing and rendering content as soon as the first chunks arrive, rather than waiting for everything. This can lead to more efficient use of browser resources.
  • User Experience: With Streaming, users are less likely to face a blank screen or loading spinner for extended periods. Instead, they see content populate incrementally, which can keep them engaged and reduce bounce rates.

Streaming in Next.js represents a significant shift in how content is delivered to the user, emphasizing speed and responsiveness. It's a response to the need for faster, more efficient web applications, especially in a world where mobile usage and slower network conditions are common. By enabling content to be streamed and rendered incrementally, Next.js offers a more modern, user-friendly approach to loading web content, enhancing both the perceived and actual performance of web applications.

Traditional Approach Without Server Actions

Before the introduction of Server Actions, handling server-side logic in Next.js typically involved a few standard approaches:

  • API Routes: Next.js allows the creation of API routes by adding files to the pages/api directory. These routes act as server-side functions that handle HTTP requests (GET, POST, etc.) and can interact with databases, external APIs, and other server-side tasks.
  • External Server/API: Many applications rely on an external server or third-party APIs for server-side logic. This setup requires managing a separate server or service, handling CORS (Cross-Origin Resource Sharing), and dealing with network requests from the client-side to the server.
  • Client-Side Data Fetching: In typical SPAs (Single Page Applications), client-side data fetching is common, where the browser fetches data from an API after the initial page load. This approach often leads to additional loading times and complexities in managing client-side state.

Introduction of Server Actions in Next.js

Server Actions in Next.js provide a streamlined way to handle server-side logic directly within your Next.js application. This feature enhances the ability to perform server-side operations without the need for additional API routes or external servers.

Key Features of Server Actions:

  • Direct Server-Side Operations: Server Actions allow you to write server-side functions that can directly handle data mutations, form submissions, and other server-side tasks, right within your Next.js application.
  • Simplified Data Flow: They simplify the data flow in your application by reducing the need for separate API endpoints. This can lead to a more straightforward and maintainable codebase.
  • Improved Performance and UX: By handling actions on the server, Server Actions can reduce the number of client-server round trips, potentially improving application performance and user experience.
  • Enhanced Security: Server Actions can enhance security by keeping sensitive logic on the server, reducing exposure to the client-side.

How Do Server Actions Differ From Traditional Approaches?

  • Reduced Complexity: Server Actions eliminate the need for separate API routes for certain server-side operations, simplifying the architecture of your application.
  • Streamlined Development Process: Developers can handle more server-side logic directly within the Next.js framework, leading to a more integrated and efficient development process.
  • Performance Optimization: By reducing the need for additional network requests for server-side operations, Server Actions can optimize performance, especially in scenarios with frequent server interactions.
  • Enhanced Security: Since Server Actions are executed on the server, they offer a more secure way to handle sensitive operations compared to client-side implementations.


Server Actions in Next.js represent a paradigm shift in handling server-side logic within Next.js applications. They offer a more integrated, efficient, and secure way to manage server-side interactions, reducing complexity and potentially enhancing performance. This feature is particularly beneficial for applications requiring frequent server-side data mutations or operations, as it streamlines the process and consolidates the server-side logic within the Next.js environment.

Now that we have seen the explanation of each of these new features, let's see them in practice on the following Next.js application with examples for each of them https://github.com/ReyRod/next-latest-features. We have leveraged on the TMDB API to have some data to use while we review these examples.

Once you download the project you can follow the Readme.MD to start it. Once started you will see this:

Let’s start by reviewing example 1, which is the new Router, we have prepared two pages each one using the old Page Router and the new App Router, so you can clearly see the differences in the code, on the UI there will be no changes between the two:


On the Page Router example we can see a definition of a page which follows the common Next.js pages folder structure, whose files and folders define the available routes in the application. The page in this case is a React component and since it does not have any of the methods getStaticProps, getStaticPaths or getServerSideProps it behaves like a simple client component. This component uses the useRouter hook to access query params from the url to display it within the component.


On the App Router example we can see some differences, the new App Router folder structure requires a specific convention of folder and files, folders define a segment of the route and the files define the behavior of the route. Since the App Router is made to load Server Components by default then we don’t have the ability to use a react hook in the server, so in this case the App Router automatically injects some properties into the component properties.

Let’s continue by reviewing example 2 about Components.



In this Client Component example we can observe that we are defining a client component by the use of “use client” directive, this allows interactivity by enabling the use of hooks and functions within the user’s browser. Since we have this component inside the new app folder, we are required to use some of the new next dependency for client components, so instead of using the useRouter hook we need to use the useSearchParams hook within the next/navigation dependency to be able to extract the query params. Also we have a useEffect and useTransition hooks to make possible the API request, we can see on the Network tab of the browser’s debugger each TMDB API request, one as soon as the component loads and another one when the page number changes.

In the Server Component example we are not using the “use client” directive, in this case the component will first render on the server and just the HTML content will be sent to the client when loading the route. You can also see that we are not using any React hook to fetch the content from the API, instead we are directly calling the api client method to get the content by a combination of async/await at the component and at the function level. With this approach we can prevent exposing the API key since all the API requests can be made on the server. When the page loads on the browser all the content is already loaded and displayed.

When comparing both of these approaches using web vitals we can notice some differences, on the left we have the Client Component and on the right we have the Server Component, we can notice an improvement in the following metrics: FCP, LCP. This means that the page will be ready for interactivity as soon as it loads.

Let’s now review the example 3 of Streaming.



Here we have made use of the loading.tsx file in this server component which is handled by this route segment and introduced a delay on the render of this component so we are able to see the content of the loading file, in this case a skeleton for the whole page is shown (page.tsx). After the delay of the page has finished then Next.js will stream the new content to the frontend, this component then displays two more server components, each component is wrapped on its own Suspense component and renders an skeleton when their content hasn’t been fetched yet, as soon as their content is fetched the server will once again stream the content of each component to the frontend. So in conclusion in this example we have few server components that are being streamed to the client when the content is ready, making the page ready from the initial load.

Finally we have example 4 of Server Actions, let’s see it in action.


We can see here that we have two files involved, one is called actions, in this file we have all API requests that are going to be made from the server (not the client) prefixed by the “use server” directive and on the page file we have our client component prefixed by the “use client” directive which is importing the action we declare on the server module, when this happens Next.js will automatically populate and use an internal api route so the client component can make an API request to the server and then the server will execute the final third party API request, making it possible not to expose any API credentials or avoiding CORS issues.


Conclusion


The latest updates in Next.js versions 13 and 14 mark a significant leap forward for the framework, introducing features like the App Router, Server Components, Streaming, and Server Actions. These innovations significantly enhance the efficiency, flexibility, and interactivity of building web applications.

The App Router offers a more dynamic routing approach, integrating seamlessly with React's latest features for improved performance and user experience. Server Components shift rendering to the server side, reducing JavaScript bundle sizes and optimizing content delivery, particularly for static parts of an application.

Streaming in Next.js revolutionizes content delivery with incremental rendering, enhancing perceived performance, especially on mobile and slower networks. Server Actions streamline server-side logic, simplifying the development process and optimizing application performance by reducing client-server interactions.

These features, demonstrated in practical applications with the TMDB API, showcase their effectiveness in real-world scenarios. They collectively enhance Next.js's capabilities, ensuring it remains at the forefront of web development, meeting the evolving demands for faster, more responsive, and interactive web applications.

Don't miss a thing, subscribe to our monthly Newsletter!

Thanks for subscribing!
Oops! Something went wrong while submitting the form.

Nodejs logging solutions

In this article we break down the solutions to handle app logging in Node.js

December 19, 2019
Read more ->
Nodejs

Server Side Rendering (SSR) of Create-React-App (CRA) app in 2020

How to do Server-Side Rendering (SSR) of a Create React App (CRA) in 2020.

February 19, 2020
Read more ->
React
SSR
Nodejs

Contact

Ready to get started?
Use the form or give us a call to meet our team and discuss your project and business goals.
We can’t wait to meet you!

Write to us!
info@vairix.com

Thank you! Your submission has been received!
Oops! Something went wrong while submitting the form.