Mastering Next.js: A Comprehensive Guide

Next.js is an open-source JavaScript framework that is built on top of React, a popular JavaScript library for building user interfaces. It is primarily used for building web applications, and it is often described as a React framework for server-rendered or statically generated applications. Next.js provides a set of tools and conventions to streamline the development of React-based web applications, making it easier to build fast, performant, and scalable websites.
Provides additional features that enable you to build production-ready applications. These features include routing, optimized rendering, data fetching, bundling, compiling, and more.
You don’t need to install additional packages as Next.js provides everything you need. Opinions and conventions should be followed to implement these features.
Docs | Next.js
Welcome to the Next.js Documentation.
Why learn Next.js?
Next.js simplifies the process of building a web application for production by providing features like:
Routing
API routes
Rendering — client-side and server-side
Data fetching
Styling
Optimization — Images, forms, and scripts
Dev and Prod build system
Some of the key features and concepts of Next.js include:
Server-Side Rendering (SSR): Next.js enables server-side rendering, which means that pages can be pre-rendered on the server and sent as fully formed HTML to the client, improving performance and SEO.
Static Site Generation (SSG): Next.js supports static site generation, allowing you to pre-render entire pages at build time. This results in extremely fast loading times and is great for content that doesn’t change frequently.
API Routes: You can create API endpoints within your Next.js application, allowing you to build both the frontend and backend within the same codebase.
Automatic Code Splitting: Next.js automatically splits your JavaScript code into smaller, optimized bundles. This improves performance by reducing the amount of code that needs to be loaded on the initial page load.
Image Optimization: It includes built-in support for optimizing and serving images efficiently, helping to improve performance and user experience.
File-based Routing: Routing is simple and intuitive in Next.js, as it follows a file-based approach. Creating files in the
pagesdirectory automatically generates routes for your application.
Create first Next.js App
To create a new Next.js project, you can use the following command, replacing <project-name> with the name of your project:
npx create-next-app@latest <app-name>The create-next-app command will prompt you to configure your project. You can choose whether to use TypeScript, ESLint, or Tailwind CSS. Follow the prompts and select the options that suit your project requirements.

To start the development server and see your new Next.js application in action, run the following command:
npm run devRouting
Next.js introduced a new App Router built on React Server Components. It has a file-system-based routing mechanism. Files and folders in your codebase define URL paths the user can access in the browser. A special page.js file is used to make route segments publicly accessible.
Press enter or click to view image in full size


We will take some scenarios to learn more about routing in Next.js
Scenario 1: When a user visits the root directory i.e., localhost:3000, show him the home page.
Create a page.tsx file in the src/app directory.
//src/app/page.tsx
export default function Home() {
return <h1>Home Page</h1>
}Scenario 2: When a user visits localhost:3000/about, show him the about page.
Create a page.tsx file in src/app/about directory.
//src/app/about/page.tsx
export default function About() {
return <h1>About Page</h1>
}Scenario 3: Nested Route
Press enter or click to view image in full size

We can create a routes hierarchy in Next.js by creating a folder inside a folder.
//src/app/blog/page.tsx
export default function Blog() {
return <h1>Blog Page</h1>
}
//src/app/blog/first/page.tsx
export default function firstBlog() {
return <h1>First Blog Page</h1>
}Note: It is not a good approach for complex applications
Scenario 4: Dynamic Routes
Press enter or click to view image in full size

It refers to the capability of generating pages dynamically based on the parameters provided in the URL. Instead of creating separate static pages for each possible route, you can use dynamic routing to handle patterns or parameters in the URL, making your application more flexible and scalable.
// src/app/products/[productId]/page.tsx
export default function ProductDetails({ params } : {
params: { productId: string };
}) {
return(
<>
<h1>Details about product {params.productId}</h1>
</>
)
}
Scenario 5: Nested Dynamic Routes

It involves combining the concepts of nested routes and dynamic routes to create a more complex and flexible routing structure.
//src/app/products/[productId]/reviews/[reviewId]/page.tsx
import React from "react";
export default function ReviewDetail({
params,
}: {
params: {
productId: string;
reviewId: string;
};
}) {
return (
<>
<h1>
Review {params.reviewId} for product {params.productId}
</h1>
</>
);
}Press enter or click to view image in full size


Catch all segments
Press enter or click to view image in full size

//src/app/docs/[[...slug]]/page.tsx
export default function Docs({params}: {
params: {
slug: string[];
};
}) {
if(params.slug?.length === 2){
return <h1>Viewing docs for feature {params.slug[0]} and concept {params.slug[1]}</h1>
} else if (params.slug?.length === 1){
return <h1>Viewing docs for feature {params.slug[0]}</h1>
}
return (
<h1>Docs Page</h1>
)
}
Press enter or click to view image in full size


404 page
The not-found file is used to render UI when the notFound function is thrown within a route segment.

//src/app/not-found.tsx
export default function NotFound() {
return(
<>
<h2>Page not found</h2>
<p>Could not find requested resource</p>
</>
)
}Private folders
A private folder indicates that it is a private implementation detail and should not be considered by the routing system.
The folder and all its subfolders are excluded from routing.
Private folders can be created by prefixing a folder with an underscore: _folderName.
useful in — for separating UI logic from routing logic, for consistently organizing internal files across a project, for sorting and grouping files in code editors, and for avoiding potential naming conflicts with future Next.js file conventions.
If you want to include an underscore in URL segments, you can prefix the folder name with %5F which is the URL-encoded form of an underscore.
//src/app/_lib/page.tsx
export default function PrivateRoute() {
return <h1>You can't view this in the browser</h1>
}localhost:3000/_lib will show a 404 error.
Route Groups

Allows us to logically group our routes and project files without affecting the URL path structure.
We can group authentication-related folders like login, register, and forgot password in the auth folder for good developer experience.
But now the URL path will be localhost:3000/auth/login
to prevent this, we use route groups.
just wrap the auth folder in parenthesis: (auth)
Layouts
A layout is a UI that is shared between multiple pages in the app, like the header and footer.
We can define a layout by default exporting a React component from a layout.js or layout.tsx file. The component should accept a children props that will be populated with a child page during rendering.
For example, the layout will be shared with the /dashboard and /dashboard/settings pages:

Root Layout
The root layout is applied to all routes.
export default function RootLayout({
children,
}: {
children: React.ReactNode
}) {
return (
<html lang="en">
<body>
<header>
<p>Header</p>
</header>
{childer}
<footer>
<p>Footer</p>
</footer>
</body>
</html>
)
}
Nested layout
Layouts defined inside a folder (e.g. app/dashboard/layout.js) apply to specific route segments and render when those segments are active.
Press enter or click to view image in full size

Metadata
Ensuring proper SEO is crucial for increasing visibility and attracting users.
Next.js introduced the metadata API which allows you to define metadata for each page. Metadata ensures accurate and relevant information is displayed when your pages are shared or indexed.
Configuring metadata
Export a static metadata object
Static metadata refers to information about a page that is determined at build time and does not change during runtime. This metadata is associated with a specific page and can include data such as title and description.
//src/app/about/page.tsx
export const metadata = {
title: "About Page",
};
export default function About() {
return <h1>About me</h1>;
}2. Export a dynamically generated metadata function
It refers to the ability to generate metadata for a page at runtime, based on dynamic data or conditions.
import { Metadata } from "next";
type Props = {
params: {
productId: string;
};
};
export const generateMetadata = ({ params }: Props ): Metadata => {
return {
title: `Product ${params.productID}`,
};
};
export default function ProductDetails({ params }: Props) {
return <h1>Details about product {params.productId}</h1>;
}we can use an asynchronous function in metadata
export const generateMetadata = async ({ params, }:
Props): Promise<Metadata> => {
const title = await new Promise((resolve) => {
setTimeout(() => {
resolve(`Product ${params.productId}`);
}, 100);
});
return { title: `Product ${title}`, };
}; Metadata rules
Both layout.tsx and page.tsx files can export metadata. If defined in a layout, it applies to all pages in that layout, but if defined in a page, it applies only to that page.
Metadata is read in order, from the root level down to the final page level.
When there’s metadata in multiple places for the same route, they get combined, but page metadata will replace layout metadata if they have the same properties.
title metadata
Functions: generateMetadata | Next.js
Learn how to add Metadata to your Next.js application for improved search engine optimization (SEO) and web…
The title field’s primary purpose is to define the document title. It can be either a string or an object.
//layout.tsx
export const metadata: Metadata = {
title: {
absolute: "",
default: "",
template: "%s | sitename",
},
description: "",
};default — if the child route does not specify a title, this will be the title
template — child route title name will override %s
Navigation
To enable client-side navigation Next.js provides us with the Link component. The <Link> component is a React component that extends the HTML <a> element, and it's the primary way to navigate between routes in Next.js. To use it, we need to import it from “next/link”
<Link href="/blog">Blog</Link>Navigate Programmatically

import { useRouter } from "next/navigation";
const router = useRouter();
const handleClick = () => {
console.log("Placing order");
router.push("/");
};
return (
<button onClick={handleClick}>Place Order</button>
)Templates
Templates are similar to layout in that wrap each child layout or page.
But, with templates, when a user navigates between routes that share a template, a new instance of the component is mounted, DOM elements are recreated, the state is not preserved, and effects are re-synchronized.
A template can be defined by exporting a default React component from a template.js or template.tsx file.
export default function Template({ children }: { children: React.ReactNode }) {
return <div>{children}</div>
}
loading.tsx
This file allows us to create loading states that are displayed to the user while a specific route segment’s content is loading
The loading state appears immediately upon navigation, giving users the assurance that the application is responsive and actively loading content


error.tsx
Create error UI tailored to specific segments using the file-system hierarchy to adjust granularity
Isolate errors to affected segments while keeping the rest of the application functional.
Add functionality to attempt to recover from an error without a full page reload.
'use client' // Error components must be Client Components
import { useEffect } from 'react'
export default function Error({
error,
reset,
}: {
error: Error & { digest?: string }
reset: () => void
}) {
useEffect(() => {
// Log the error to an error reporting service
console.error(error)
}, [error])
return (
<div>
<h2>Something went wrong!</h2>
<button
onClick={
// Attempt to recover by trying to re-render the segment
() => reset()
}
>
Try again
</button>
</div>
)
}
Handling errors in nested routes
An error.tsx file will cater to errors for all its nested child segments
By positioning error.tsx files at different levels in the nested folders of a route, you can achieve a more granular level of error handling
Parallel Routes
Parallel routes are an advanced routing mechanism that allows for the simultaneous rendering of multiple pages within the same layout.
Parallel routes in Next.js are defined using a feature known as slots. Slots help structure our content in a modular fashion.
To define a slot, we use the ‘@folder’ naming convention. Each slot is then passed as a prop to its corresponding ‘layout.tsx’ file.
Press enter or click to view image in full size

For example, considering a dashboard, you can use parallel routes to simultaneously render the users, Revenueand notifications pages:
// dashboard/layout.tsx
export default function DashboardLayout({
children,
users,
revenue,
notifications }:
{
children: React.ReactNode;
users: React.ReactNode;
revenue: React.ReactNode;
notifications: React.ReactNode;
}) {
return (<>
<div>{children}</div>
<div>{users}</div>
<div>{revenue}</div>
<div>{notifications}</div>
</>);
}
A benefit of parallel routes is their ability to split a single layout into various slots, making the code more manageable
Independent route handling
Each slot of your layout, such as user analytics or revenue metrics, can have its own loading and error states.
This granular control is particularly beneficial in scenarios where different sections of the page load at varying speeds or encounter unique errors.

Sub-navigation in routes
Each slots of your dashboard can essentially function as a mini-application, complete with its own navigation and state management
This is especially useful in a complex application such as our dashboard where different sections serve distinct purposes.

//dashboard/@notifications/page.tsx
export default function Notifications() {
return (
<div>Notifications</div>
<Link href="/dashboard/">Archived</Link>
)
};
//dashboard/@notifications/archieved/page.tsx
export default function ArchivedNotifications() {
return (
<div>Archived Notifications</div>
<Link href="/dashboard/">Default</Link>
)
};to be continued…..
