Next.js 14: Exploring fundamental concepts

Boost your web app performance.

2024-04-04

#Frontend

Overview

Next.js, developed by Vercel in 2016, is a powerful JavaScript framework used to create fast, responsive and SEO-friendly web applications, using the popular React library.

In this article, although Next.js 13 and 14 brought a lot of new features, I want to take a closer look on: custom routing, prefetching, differences between Server and Client Components, optimization tools, cache support, and the benefits of developer experience, SEO, and performance perspective. Additionally, I will provide a brief guide on how to install the latest version of Next.js so that you can leverage all the benefits mentioned in the article. Let's dive in!

Instalation

How to install this awesome framework in the latest next version? Here are the steps to create a new project:

  • make sure you have a package manager installed, such as npm (included with Node.js by default) or yarn
  • open your terminal or command prompt
  • in the selected directory run the Next.js project initialization command by typing: npx create-next-app@latest

This command will download the latest version of Next.js and create a new project in the current directory. During the initialization process, you'll see the following prompts:

1
2
3
4
5
6
7
8
✔ What is your project named? … next-14 //default 'my-app'
✔ Would you like to use TypeScript? … No / Yes //default Yes
✔ Would you like to use ESLint? … No / Yes //default Yes
✔ Would you like to use Tailwind CSS? … No / Yes //default No
✔ Would you like to use `src/` directory? … No / Yes //default Yes
✔ Would you like to use App Router? (recommended) … No / Yes //default Yes
✔ Would you like to customize the default import alias (@/*)? … No / Yes //default No
✔ What import alias would you like configured? … @/* //if you choose 'yes' above the next default prompt will be '@/*'

Once the initialization process is complete, you can start your new project by typing: npm run dev. That's it! You now have a Next.js project set up and running on the latest version of the framework. You can start writing code and developing your web application.

As a developer, I highly suggest using TypeScript in your projects. It's super handy and helps catch bugs before they become big headaches. Also, don't forget about ESLint – it's keeps things tidy and error-free. Tailwind CSS is awesome for making your components look sleek, but if you don't know this tool, there are other cool tools like Styled-Components or styling ways mentioned in the documentation. The src directory structure proves beneficial for project organization and ease of navigation. It is highly recommended that you select the Router app as suggested for installation. This choice ensures compatibility with Next.js' modern routing approach, thus optimizing the routing experience. While customizing default import aliases is not recommended by default, I think it saves loads of typing and makes your code look cleaner.

How to manage routing

Until the release of Next.js version 13, routing was based on the Pages directory. With the new version of Next.js, the App Router has been introduced, supporting shared layouts, nested routing, loading states, error handling, and more of React's latest features, such as Server Components and Streaming.

App Router

In app directory, other folders define routes, and each folder in a route represents a route segment. Each route segment is mapped to a corresponding segment in a URL path.

Example of folder directory:

1
2
3
4
5
6
app
├── about
│   └── page.js
└── account
    └── settings
        └── page.js

See the result below:

view of page url

Files like page.js are used to create UI for a route segment, but Next.js provides a set of special files to create UI with specific behavior in nested routes include:

  • layout: create shared UI for a segment and its children
  • page: as mention above, it creates a unique UI for a route to make routes publicly accessible
  • loading: this file is responsible for creating the loading UI with React Suspense. It will automatically wrap the page.js file and all its nested components within a <Suspense> boundary
  • not-found: it is UI for handling not-found cases for a segment and its children
  • error: UI for handling errors for a segment and its children
  • global-error: Global error UI
  • route: Server-side API endpoint
  • template: very similar to layouts, it is create a new instance when navigating between pages that use the same template
  • default: Fallback UI for Parallel Routes

Special files are described in more detail in the in the documentation.

It is also worth mentioning that Next.js automatically handles important scenarios like Loading, Error, and Not-Found depending on where these files are located.

view of components hierarchy

As seen in the image above React components that are defined in special route segment files are always rendered in a specific hierarchy, and in the case of a nested route, the segment components will be nested inside the components of its parent segment.

In the App Router, the app directory coexists with the pages directory, which is a good approach, as it allows for a gradual migration between versions. However, it's important to note that the app directory takes precedence over the pages directory, and naming folders the same way in both directories will result in a compilation error.

Prefetching

Prefetching in Next.js is an advanced mechanism that ensures faster and smoother transitions between pages by preloading data or resources in the background before they are actually needed by the user. It works as a kind of anticipation - the application predicts which resources will be needed in the future and loads them in advance. This way, when a user clicks on a link or navigates to a new page, the content is already loaded and ready to be displayed, eliminating delays and providing a seamless interaction.

In Next.js, routes can be prefetched using two methods:

  • <Link> Component: Routes are automatically prefetched when they come into the user's viewport. This prefetching occurs either during the initial page load or when the route becomes visible through scrolling
  • router.prefetch(): Another method involves using the useRouter hook to programmatically prefetch routes. This allows developers to have more control over when and which routes are prefetched

Prefetching in Next.js is a powerful tool that significantly improves the user experience by providing faster and more responsive application interfaces. Additionally, it gives developers the ability to optimize application loading by preparing resources in advance, resulting in user satisfaction and better SEO performance.

Server vs Client Components

Server Components and Client Components play a crucial role in determining how your application renders and delivers content to users. React and Next.js allow you to create hybrid web applications where parts of your code can be rendered on the server or the client. By default, Next.js uses Server Components. This allows you to automatically implement server rendering with no additional configuration. Here's a breakdown of each:

Server Components

Server Components are rendered exclusively on the server-side. They are responsible for generating pre-rendered HTML, which is then sent to the browser. This approach eliminates the need for a large JavaScript bundle on the client-side, resulting in a smaller initial page load size. Server Components are ideal for delivering content that doesn't require dynamic updates or interactivity after the initial render. They contribute to improved performance and security by minimizing client-side processing. More about Server Components you can find in documentation.

Client Components

Client Components are rendered both on the server and the client. During the initial request, Client Components are rendered on the server and sent to the browser. Subsequently, for client-side navigations, these components are rendered only on the client. This process, known as "hydration," transforms the initially static markup into interactive content. Client Components are typically used for elements that require dynamic updates and interactivity, such as animations or user interactions. While they enhance the user experience, they may not directly impact SEO, as the core content is already defined in the server-rendered HTML.

To use Client Components, simply add the "use client" React directive at the very top of the file, before imports.

1
2
3
4
5
'use client'

import { useState } from 'react'

... // the rest of your code

“Use client” is used to declare the boundary between the Server and Client Components modules. Please note that once a "use client" is defined in a file, all other modules imported into it, including child components, are considered part of the client package.

More about client component you can find in documentation.

Next.js built-in optimizations

Next.js provides several built-in optimizations such as:

  • code splitting: The process of dividing an application's code into smaller packages that can be downloaded and run through a
    browser. Each chunk contains the code required for a particular page. This reduces the amount of data transferred and the turnaround time for each request, leading to improved performance.
  • image optimization: With features like lazy loading and blur-up placeholders, the Image component ensures faster page loads and prevents layout shifts during image loading, enhancing visual stability. It supports both local and remote image sources, allowing for on-demand image resizing and customization. To use the Image component, simply import it and provide the image source. For local images, Next.js automatically determines the width and height, while for remote images, you need to specify these manually. Additionally, you can customize image loading priorities to optimize Largest Contentful Paint (LCP) performance.
  • image optimization: With features like lazy loading and blur-up placeholders, the Image component ensures faster page loads and prevents layout shifts during image loading, enhancing visual stability. It supports both local and remote image sources, allowing for on-demand image resizing and customization. To use the Image component, simply import it and provide the image source. For local images, Next.js automatically determines the width and height, while for remote images, you need to specify these manually. Additionally, you can customize image loading priorities to optimize Largest Contentful Paint (LCP) performance.
  • font optimization: eliminating external network requests and enhancing both performance and privacy. It facilitates self-hosting of fonts, including Google Fonts, ensuring zero layout shift and improved loading times. By importing fonts from next/font/google and defining subsets and display properties, you can easily integrate Google Fonts into your application. Fonts are downloaded at build time and served from the same domain, reducing reliance on external requests. For local fonts, next/font/local enables self-hosting by specifying the font source and display properties. Multiple font files for a single family can be managed efficiently, improving performance.

Using above optimizations improves application compilation performance. More about them under this link.

Cache

Cache in Next.js is a crucial mechanism that influences application performance and reduces costs by storing the results of rendering and data requests. There are four main caching mechanisms: request memoization, data cache, full route cache and route cache. Each of these mechanisms has its own purpose and impact on the application.

Request Memoization

Request memoization allows for storing the results of fetch function calls, enabling reuse of the same data in different parts of the React component tree without the need for re-fetching. This is particularly useful when the same data is used in multiple areas of the application.

Data Cache

The data cache in Next.js stores the results of data requests, allowing for their reuse across different user requests and application versions. It also enables configuring revalidation periods, allowing for data updates in the background.

Cached data can be revalidated in two ways, with:

  • Time-based Revalidation: Revalidate data after a certain amount of time has passed and a new request is made. This is useful for data that changes infrequently and freshness is not as critical
  • On-demand Revalidation: Data revalidation occurs in response to specific events, such as a form submission. On-demand revalidation can use a tag-based or path-based approach to revalidate groups of data at once. This is useful when you want to ensure the latest data is shown as soon as possible, particularly in scenarios like updating content from a headless CMS
Full Route Cache

The full route cache in Next.js stores the results of rendering pages in memory, enabling fast serving of statically generated pages without the need for re-rendering on each request. This is a key optimization mechanism that speeds up page loading and reduces server load.

Router Cache

This in-memory client-side cache stores the React Server Component Payload, split by individual route segments, for the duration of a user session. The Router Cache enhances navigation experiences by storing visited route segments and prefetching routes the user is likely to navigate to, based on <Link> components in their viewport.

Note: It's also important to understand how caching mechanisms interact with each other and how they can be managed for optimal application performance. For example, data revalidation may affect other caching mechanisms, so conscious management of cache configuration in the application is crucial. You can read more about cache in the documentation

Conclusion

Next.js 14 introduces powerful features and optimizations that enhance both developer experience and application performance. With its customizable routing system, advanced prefetching capabilities, and support for both Server and Client Components, Next.js offers a flexible and efficient solution for building modern web applications.

The introduction of the App Router streamlines navigation and layout management, making it easier to organize and maintain complex application structures. Prefetching mechanisms ensure smooth transitions between pages, improving user experience and reducing loading times.

The distinction between Server and Client Components enables developers to fine-tune rendering strategies based on performance and SEO requirements. By leveraging built-in optimizations such as code splitting, image optimization, and font loading, developers can further optimize application loading times and user interaction.

Additionally, Next.js provides robust caching mechanisms to improve application performance and reduce server load. By intelligently caching data and rendered content, Next.js minimizes redundant network requests and enhances scalability.

Overall, Next.js continues to evolve as a leading framework for building fast, responsive, and SEO-friendly web applications. Its comprehensive feature set and focus on performance make it a valuable tool for developers seeking to deliver exceptional user experiences in today's competitive digital landscape.

Naturally, we also use them in our software development company.

Share this post

Related posts

Frontend

2024-05-20

Pigment CSS - new library from Material UI

Frontend

2024-04-24

React 19 has been officially announced!

Frontend

2023-09-20

Website Accessibility

Want to light up your ideas with us?

Józefitów 8, 30-039 Cracow, Poland

hidevanddeliver.com

(+48) 789 188 353

NIP: 9452214307

REGON: 368739409