📚 BusinessExplain 🏠 Home 📊 Analytics
← Back to Skills

Nextjs Mastery

💻 Coding & Engineering 12 min read v1.0.0

Next.js Mastery

Quick Reference

| Concern | Key API / Pattern |
|---|---|
| App Router layouts | app/layout.tsx, template.tsx |
| Loading / Error UI | loading.tsx, error.tsx, global-error.tsx |
| Parallel routes | @folder slot convention |
| Intercepting routes | (.) / (..) / (...) folder convention |
| Server Components | default in app/; "use client" directive |
| SSR / SSG / ISR | dynamic, revalidate, generateStaticParams |
| Streaming | + async Server Components |
| Server Actions | "use server" functions |
| Route Handlers | app/api/route.ts |
| Middleware | middleware.ts at project root |
| Caching | fetch options, unstable_cache, revalidateTag |
| Security | CSP headers, CSRF tokens, rate limiting, Zod validation |


1. App Router Architecture

1.1 Special File Conventions

| File | Purpose |
|---|---|
| layout.tsx | Shared UI wrapping children; persists across navigations |
| template.tsx | Like layout but re-mounts on every navigation |
| loading.tsx | Suspense fallback while segment loads |
| error.tsx | Error boundary (must be client component) |
| global-error.tsx | Catches root layout errors |
| not-found.tsx | Shown when notFound() is thrown |
| default.tsx | Fallback for unmatched parallel routes |

// app/layout.tsx — root layout (required)
export default function RootLayout({ children }: { children: React.ReactNode }) {
  return (
    
      
        
        
{children}
); }

// app/dashboard/error.tsx
"use client";
export default function DashboardError({ error, reset }: { error: Error & { digest?: string }; reset: () => void }) {
return (


Something went wrong!




);
}

1.2 App Router Request Flow

flowchart TD
  A["Browser Request"] --> B["middleware.ts"]
  B -->|rewrite/redirect/continue| C["App Router Match"]
  C --> D["Root Layout"]
  D --> E["Nested Layouts"]
  E --> F["Page / Template + loading.tsx"]
  F --> G{"Data Fetching"}
  G -->|Server Component| H["Await async data"]
  G -->|Client Component| I["Render + Hydrate"]
  H --> J["Stream HTML via Suspense"]
  I --> J
  J --> K["Complete Response"]
  K --> L["Client Hydration"]

style A fill:#e1f5fe
style B fill:#fff3e0
style L fill:#e8f5e9

1.3 Parallel Routes

Named slots (@folder) render multiple pages in one layout simultaneously:

// app/dashboard/layout.tsx
export default function DashboardLayout({
  children, analytics, team,
}: { children: React.ReactNode; analytics: React.ReactNode; team: React.ReactNode }) {
  return (
    
{children}
{analytics}
{team}
); } // app/dashboard/@analytics/page.tsx → renders in analytics slot // app/dashboard/@team/page.tsx → renders in team slot

1.4 Intercepting Routes

Modal context in-app, full page on direct URL:

app/dashboard/photos/[id]/page.tsx        ← full page (direct URL)
app/dashboard/(.)photos/[id]/page.tsx    ← modal (intercepted, same-level)

2. Rendering Strategies

2.1 Rendering Decision Tree

flowchart TD
  A["Component"] --> B{"Browser APIs / interactivity?"}
  B -->|Yes| C["Client Component
'use client'"] B -->|No| D{"Data fetch?"} D -->|No| E["Static Server Component
(SSG at build)"] D -->|Yes| F{"Real-time?"} F -->|No| G{"Revalidation interval?"} G -->|None| H["SSG: generateStaticParams"] G -->|Timed| I["ISR: revalidate=N"] F -->|Yes| J{"Defer parts?"} J -->|Yes| K["Streaming SSR + Suspense"] J -->|No| L["Full SSR dynamic='force-dynamic'"]

style C fill:#ffcdd2
style E fill:#c8e6c9
style I fill:#fff9c4
style K fill:#bbdefb

2.2 SSR, SSG, ISR, Streaming

// ISR: revalidate every hour
export const revalidate = 3600;
export default async function ProductsPage() {
  const products = await db.product.findMany({ take: 20 });
  return 
    {products.map(p =>
  • {p.name}
  • )}
; }

// SSR: always server-render
export const dynamic = "force-dynamic";
export default async function DashboardPage() {
const stats = await fetch("https://api.example.com/stats", { cache: "no-store" }).then(r => r.json());
return ;
}

// SSG: static at build time
export function generateStaticParams() {
return db.post.findMany({ select: { slug: true } });
}
export default function BlogPost({ params }: { params: { slug: string } }) {
return

;
}

// Streaming with Suspense — progressive page load
import { Suspense } from "react";
export default function DashboardPage() {
return (


Dashboard


}>
}>

);
}
async function RevenueChart() {
const data = await fetchRevenue();
return ;
}


3. Data Fetching Patterns

3.1 Server Actions

// app/actions/create-post.ts
"use server";
import { z } from "zod";

const schema = z.object({ title: z.string().min(1).max(200), content: z.string().min(1) });

export async function createPost(formData: FormData) {
const result = schema.safeParse({
title: formData.get("title") as string,
content: formData.get("content") as string,
});
if (!result.success) return { error: result.error.flatten().fieldErrors };

await db.post.create({ data: result.data });
revalidatePath("/blog");
redirect("/blog");
}

// app/blog/new/page.tsx — client component using Server Action
"use client";
import { useFormState, useFormStatus } from "react-dom";
import { createPost } from "@/app/actions/create-post";

function SubmitButton() {
const { pending } = useFormStatus();
return ;
}

export default function NewPostPage() {
const [state, formAction] = useFormState(createPost, null);
return (