Of course! Here are the Next.js tutorials you requested, following your specified format.
Next.js — Intro: pages/app router concepts (routing basics)
- Difficulty: Beginner 🔰
- Description: Understand the two fundamental ways to build routes in Next.js. This tutorial covers the file-system-based routing concepts for both the classic Pages Router and the modern App Router.
- Time to Read: 8 minutes
Content
🤔 The Core Idea: File-System Routing
Next.js revolutionized routing by getting rid of complex configuration files. Instead, you create routes simply by adding files and folders to your project. The structure of your file system directly maps to the URL paths of your application. Next.js offers two ways to do this.
1. The Pages Router (The Classic Approach)
This was the original routing system in Next.js. All your routes are defined inside a special pages directory.
- Basic Routes: A file in the pages directory becomes a route.
- pages/index.js → /
- pages/about.js → /about
- Nested Routes: A folder structure creates nested routes.
- pages/blog/first-post.js → /blog/first-post
- Dynamic Routes: To create a route that can handle variable segments (like a user ID or a blog post slug), you use square brackets [] in the filename.
- pages/blog/[slug].js → Matches /blog/a-great-post, /blog/another-one, etc. The value of slug is available as a query parameter in your component.
Code Example (pages/blog/[slug].js):
JavaScript
import { useRouter } from 'next/router';
const PostPage = () => {
  const router = useRouter();
  const { slug } = router.query; // Access the dynamic part of the URL
  return <p>Reading post: {slug}</p>;
};
export default PostPage;
2. The App Router (The Modern Approach)
Introduced in Next.js 13, the App Router is built on top of React Server Components and is the recommended approach for new applications. It lives in an app directory.
- Routes are Folders: Instead of files, folders now define the route segments.
- The page.js file: The UI for a specific route is defined in a special file named page.js inside its corresponding folder.
- app/about/page.js → /about
- Layouts: The App Router introduces a powerful concept of layouts. A layout.js file defines a UI that is shared across multiple pages in a segment.
- app/dashboard/layout.js → A shared sidebar for all dashboard pages.
- app/dashboard/settings/page.js → The settings page, which appears inside the dashboard layout.
- Dynamic Segments: Folders use square brackets [] to create dynamic routes.
- app/blog/[slug]/page.js → Matches /blog/a-great-post, etc. The slug is passed as a prop.
Code Example (app/blog/[slug]/page.js):
JavaScript
// The params object is passed directly as a prop to the page component
function PostPage({ params }) {
  const { slug } = params; // Access the dynamic part of the URL
  return <h1>Reading post: {slug}</h1>;
}
export default PostPage;
Linking Between Pages
Regardless of which router you use, you should always use the <Link> component from next/link for client-side navigation. This prevents a full page reload and provides a much faster, app-like experience.
JavaScript
import Link from 'next/link';
function Navbar() {
  return (
    <nav>
      <Link href="/">Home</Link>
      <Link href="/about">About Us</Link>
      <Link href="/blog/my-first-post">Blog</Link>
    </nav>
  );
}
Quiz
Question: Using the App Router, which file path would correctly create the page for the URL /dashboard/users?
Options: A) app/dashboard/users.js B) pages/dashboard/users.js C) app/dashboard/users/page.js D) app/dashboard/users/index.js
Answer: C) app/dashboard/users/page.js
Explanation: In the App Router, routes are defined by folders. The dashboard folder contains the users folder, creating the /dashboard/users path. The actual UI for that path must be defined in a file named page.js inside the final folder segment.
Next.js — Data fetching patterns: SSR, SSG, ISR, Client fetch
- Difficulty: Intermediate ⚙️
- Description: Master the art of loading data in Next.js. This tutorial breaks down the four core data fetching strategies—SSG, SSR, ISR, and Client-side—explaining what they are and when to use each for optimal performance.
- Time to Read: 10 minutes
Content
Next.js is a "hybrid" framework, meaning you can choose how to render your pages on a case-by-case basis. Choosing the right data fetching pattern is key to building a fast and efficient application.
1. Static Site Generation (SSG)
- What it is: The page's HTML is generated at build time. When a user requests the page, they are served a pre-built static file from a CDN.
- When to use it: For content that is the same for every user and doesn't change often. Think blog posts, marketing pages, documentation, and product catalogs.
- Result: Blazing fast performance (no server computation needed on request), great for SEO.
Code Example (App Router): In the App Router, SSG is the default behavior! Next.js automatically caches the result of fetch calls.
JavaScript
// app/posts/[slug]/page.js
async function getPost(slug) {
  const res = await fetch(`https://api.example.com/posts/${slug}`);
  return res.json();
}
// This page will be statically generated at build time for each post.
export default async function PostPage({ params }) {
  const post = await getPost(params.slug);
  return <div>{post.content}</div>;
}
2. Server-Side Rendering (SSR)
- What it is: The page's HTML is generated on the server for every single request.
- When to use it: When the page displays frequently changing data or content that is unique to the logged-in user. Think user dashboards, social media feeds, or e-commerce checkout pages.
- Result: The page is always up-to-date, but it's slower than SSG because the server has to do work on every request.
Code Example (App Router): To opt into SSR, you can access a dynamic function like headers() or cookies(), or configure a fetch request to not use the cache.
JavaScript
import { cookies } from 'next/headers';
// app/dashboard/page.js
async function getUserData(sessionToken) {
  const res = await fetch('https://api.example.com/user', {
    headers: { 'Authorization': `Bearer ${sessionToken}` }
  });
  return res.json();
}
// This page is server-rendered on every request because it uses cookies().
export default async function Dashboard() {
  const sessionToken = cookies().get('session')?.value;
  const userData = await getUserData(sessionToken);
  return <h1>Welcome, {userData.name}</h1>;
}
3. Incremental Static Regeneration (ISR)
- What it is: The best of both worlds. The page is statically generated at build time, but it can be automatically re-generated in the background after a certain amount of time has passed.
- When to use it: For content that is mostly static but needs to be updated periodically. Think a news site's homepage, an e-commerce product page with changing stock levels, or a popular blog post you might want to edit.
- Result: You get the speed of a static site, but without needing to rebuild your entire site to update content.
Code Example (App Router): ISR is configured using the revalidate option in the fetch call.
JavaScript
// app/products/[id]/page.js
async function getProduct(id) {
  // Re-fetch this data at most once every 60 seconds
  const res = await fetch(`https://api.example.com/products/${id}`, {
    next: { revalidate: 60 }
  });
  return res.json();
}
export default async function ProductPage({ params }) {
  const product = await getProduct(params.id);
  return <h1>{product.name} - ${product.price}</h1>;
}
4. Client-Side Fetching (CSR)
- What it is: The traditional React way. The page is initially rendered with a loading state (statically or from the server), and the data is fetched in the browser using useEffect.
- When to use it: For user-specific data that is not needed for the initial render or for SEO. Think a user settings page within a larger dashboard, or a "likes" count on a blog post.
- Result: The initial page load is fast, and data is populated as it arrives.
Code Example (App Router): You must mark the component with "use client" since it uses hooks.
JavaScript
"use client";
import { useState, useEffect } from 'react';
import useSWR from 'swr'; // A popular data-fetching library
const fetcher = url => fetch(url).then(res => res.json());
export default function UserLikes() {
  const { data, error } = useSWR('/api/likes', fetcher);
  if (error) return <div>Failed to load</div>;
  if (!data) return <div>Loading...</div>;
  return <div>Likes: {data.count}</div>;
}