Next.js - An introduction

A quick introduction to Next.js, a React framework for building server-side rendered and static web applications.

# nextjs # javascript # react # framework # server-side rendering # static site generation

Next.js logo

Introduction

Next.js is a React framework for building server-side rendered and static web applications that has gained widespread adoption among developers due to its advanced features and ease of use.

Its features include hybrid rendering, dynamic routing and image optimization, among others. Let’s take a look at some of these features, particularly the new ones introduced in the latest 13 release.

What is Next.js?

Next.js was created by Vercel, a company that provides hosting and deployment services for web applications, and was first released in 2016 as an open-source project.

As a React framework, it provides a set of tools and conventions that make it easier to build React applications, such as a file-based routing system and data fetching methods. It also provides tools and configurations for bundling and compiling the application, using the Next.js Compiler written in Rust using SWC.

nextjs schema Image from Next.js documentation

Why use Next.js?

As previously mentioned, Next.js is a React framework, meaning that it is built on top of React and offers additional structure, features and optimizations. You can use React to build your UI and incrementally adopt Next.js features to solve common application requirements, which in most cases will be a better choice than building your own solutions or using third-party libraries.

There are several reasons why you should consider using Next.js:

  • Performance - Next.js offers better performance with server-rendered pages.
  • SEO - Next.js enables pre-rendering, which results in a set of static HTML pages that can be indexed by search engines.
  • The benefits of a server - Next.js provides benefits similar to having a server in place, such as caching, routing, API routes, etc.
  • Easy deployment - Next.js is well-suited for static sites and integrates easily with CI/CD tools pipelines.

I will now take a look at some of the features that make Next.js a great choice.

Features

Routing

Starting from version 13, the routing system is built on React Server Components and, as in previous versions, is based on the file system. This means that the file system is used to define the routes of the application, and that the routes are automatically generated from the file system structure.

The core of the routing system is the app folder, at the root of the project.

Inside this folder, you can create any number of subfolders, and each of them will be mapped to a route. For example, if you create a dashboard folder inside the app folder, you will be able to access the dashboard page at the /dashboard route.

You can have nested routes by creating subfolders inside the dashboard folder, and you can also use dynamic routes by creating a folder with the name of the route prefixed with [ and suffixed with ]. For example, if you create a folder named users inside the dashboard folder, you will be able to access the /dashboard/users route, and if you create a folder named [id] inside the users folder, you will be able to access the /dashboard/users/:id route.

Next.js provides a set of special files that can be used to define specific behaviors for the routes:

  • page.js - the public UI of the route
  • layout.js - the shared layout for the route and its children
  • loading.js - wraps the page in the React Suspense component
  • error.js - wraps the page in the ErrorBoundary component
  • not-found.js - the UI for the 404 page

Rendering

Next.js provides Server and Clients Components, meaning that the application can be rendered on the server or on the client, depending on what the developer wants.

There are four types of rendering:

  • Client-side Rendering - the component is rendered on the client
  • Server-side Rendering - the component is generated on the server for each request
  • Static Rendering - the HTML is generated at build time, cached and served to the client, and reused for each request
  • Dynamic Rendering - both server and client components are rendered on the server, and the client components are hydrated on the client

Data Fetching

Next.js has extended the fetch() API and uses async/await with React Server Components.

This means you can fetch data inside components, as in these examples taken from the Next.js blog:

// app/page.js
async function getData() {
  const res = await fetch('https://api.example.com/...');
  // The return value is *not* serialized
  // You can return Date, Map, Set, etc.
  return res.json();
}

// This is an async Server Component
export default async function Page() {
  const data = await getData();

  return <main>{/* ... */}</main>;
}

And you can also set and configure the caching behavior:

// This request should be cached until manually invalidated.
// Similar to `getStaticProps`.
// `force-cache` is the default and can be omitted.
fetch(URL, { cache: 'force-cache' });

// This request should be refetched on every request.
// Similar to `getServerSideProps`.
fetch(URL, { cache: 'no-store' });

// This request should be cached with a lifetime of 10 seconds.
// Similar to `getStaticProps` with the `revalidate` option.
fetch(URL, { next: { revalidate: 10 } });

Data can be fetched inside layout components as well, and the data is shared with the children components. You can use the loading and errors components to handle the request status on the UI.

Image Optimization

Next.js provides a built-in image optimization feature that allows you to optimize images for different devices and screen sizes, and to serve them in the most efficient way possible, improving the Core Web Vitals metrics.

With the Image component, you simply have to provide the local path to the image, and Next.js will automatically optimize it for you. You don’t need to provide width and height attributes for local images, as Next.js will automatically calculate them for you.

import Image from 'next/image'
import profilePic from '../public/profile.jpg'

function Home() {
  return (
    <>
      <h1>My Homepage</h1>
      <Image
        src={profilePic}
        alt="Picture of me"
      />
      <p>Welcome to my homepage!</p>
    </>
  )
}

export default Home;

The loader function generates the URLs of the optimized image, and the Image component automatically generates the srcset attribute for the img tag, like in this example:

<img
  src="/_next/image?url=%2Fprofile.jpg&w=1080&q=75"
  srcset="/_next/image?url=%2Fprofile.jpg&w=1080&q=75 1x,
          /_next/image?url=%2Fprofile.jpg&w=1080&q=75&dpr=2 2x"
  alt="Picture of me"
/>

Middleware

Next.js provides a middleware system that allows you to add custom logic to the request/response cycle.

For example, you can add headers to the response:

// middleware.ts
import { NextRequest, NextResponse } from 'next/server'

export function middleware(req: NextRequest) {
  return NextResponse.next({
    headers: {
      'x-custom-header': 'hello world',
    },
  })
}

Conclusion

Next.js is a powerful framework that allows you to build modern web applications and a series of built-in features that make the development process easier and faster, SEO-friendly, and with great performances.

I wanted to give you a quick overview of the main features of Next.js, and why I think it’s a great choice over other JavaScript frameworks for your next project, but there are many other features that I didn’t cover, so if you want to learn more, I suggest you to check out the official documentation and the Next.js blog.

I hope you enjoyed this article, and if you have any questions or feedback, feel free to reach out to me on Twitter!