Mojtaba Pourkhanlar
About meProjectsBlog

  • 👤About me
  • 🧰Projects
  • ✍️Blog

Next.js Questions


CSR, SSR, SSG, PPR, ISR

CSR (Client-Side Rendering):

  • رندر سمت مرورگر بعد از دانلود جاوااسکریپت.
  • مناسب داشبوردها و اپلیکیشن‌های کاملاً تعاملی
"use client";

import { useEffect, useState } from "react";

export default function CSRPage() {
  const [data, setData]
 = useState(null);

  useEffect(() => {
    fetch("/api/hello")
      .then(r => r.json())
      .then(d => setData(d));
  }, []
);

  return (
    <div>
      <h1>CSR Page</h1>
      <p>{data ? data.message : "Loading..."}</p>
    </div>
  );
}

SSR (Server-Side Rendering):

  • خب HTML در هر درخواست روی سرور ساخته می‌شود
  • مناسب صفحات شخصی‌سازی‌شده (پروفایل، پنل کاربری)
export default async function SSRPage() {
  const res = await fetch("https://api.example.com/data", {
    cache: "no-store", // کلید طلایی SSR
  });
  const data = await res.json();

  return (
    <div>
      <h1>SSR Page</h1>
      <p>{data.title}</p>
    </div>
  );
}

SSG (Static Site Generation):

  • صفحه یک بار در build-time ساخته میشه و روی سرور کش میشه، و همیشه همون فایل html سرو میشه
  • سریع، امن و عالی برای SEO
export const dynamic = "force-static";

export default async function SSGPage() {
  const data = await fetch("https://api.example.com/data").then(r => r.json());

  return (
    <div>
      <h1>SSG Page</h1>
      <p>{data.title}</p>
    </div>
  );
}

ISR (Incremental Static Regeneration):

  • همون SSG هستش که صفحه استاتیکه ولی هر X ثانیع یکبار دوباره بروزرسانی میشه
  • بازسازی صفحه بدون rebuild کل پروژه
  • ترکیب SSG و SSR
export const revalidate = 10; // هر 10 ثانیه صفحه re-generate می‌شود

export default async function ISRPage() {
  const data = await fetch("https://api.example.com/data").then(r => r.json());

  return (
    <div>
      <h1>ISR Page</h1>
      <p>{data.title}</p>
      <small>Regenerate every 10 sec</small>
    </div>
  );
}

PPR (Partial Prerendering) :

بخشی از صفحه سرور استاتیک میاد، بخش‌هایی که نیاز به دیتا دارن به شکل تعلیقی و استریم بعداً لود می‌شن.

این روش برای صفحات بزرگ‌تر فوق‌العاده‌ست

  • تركيب محتواى استاتيک و دايناميک دريک درخواست HTTP
  • بهينهسازى عملكرد با استريم همزمان كاميوننتهاى دايناميک
import { Suspense } from "react";

// این بخش async بعداً stream می‌شود
async function DynamicSection() {
  const res = await fetch("https://api.example.com/slow-data", {
    cache: "no-store",
  });
  const data = await res.json();

  return <p>Dynamic data: {data.title}</p>;
}

export default function PPRPage() {
  return (
    <div>
      <h1>Partial Prerendering (PPR)</h1>

      {/* این بخش استاتیک است */}
      <p>This section is pre-rendered instantly.</p>

      {/* این بخش بعدا stream می‌شود */}
      <Suspense fallback={<p>Loading dynamic section...</p>}>
        <DynamicSection />
      </Suspense>
    </div>
  );
}

نکته: ISR بهترین انتخاب برای بلاگ‌ها، لندینگ‌ها و محتواهای نیمه‌داینامیک است.


Caching Layers

خب Next.js به طور پیش‌فرض fetch را cache می‌کند.

اما Next چند لایه کش داره

Request Memoization

در یک رندر اگر چند بار یک fetch انجام بدی Next فقط یکبار request می‌زند.

await fetch("/api/products");
await fetch("/api/products");

Data Cache

وقتی fetch می‌کنی Next پاسخ API را cache می‌کند.

fetch(url, { cache: "force-cache" });
// Or
fetch(url, { next: { revalidate: 60 } }); // هر 60 ثانیه revalidate

بهش میگن:

ISR (Incremental Static Regeneration)

Full Route Cache

اگر صفحه static باشد Next کل خروجی HTML را cache می‌کند.

Router Cache (Client)

در client navigation Next صفحات را در حافظه مرورگر cache می‌کند

بنابراین back navigation سریع می‌شود.

<Link />

Route Handler

جایگزین API Routes قدیمی در App Router است.

app / api / users / route.ts;

Server Actions در Next.js 14/15

اجازه می‌دهد تابع server را مستقیم از form صدا بزنیم بدون API.

یا Server Actions اجازه می‌دهند مستقیم یک function server را صدا بزنی.

"use server";

export async function createUser(formData: FormData) {
  const name = formData.get("name");

  await db.user.create({
    data: { name },
  });
}

استفاده در فرم:

<form action={createUser}>
  <input name="name" />
  <button type="submit">Save</button>
</form>

مزایا:

  • حذف API layer
  • امنیت بیشتر
  • performance بهتر

Pages Router Vs App Router

Page Router App Router
Old New
getServerSideProps Fetch مستقیم
API Router route handlers
layout محدود nested layouts
React 18 features کمتر streaming/suspense

Hydration Error

اگر خروجی سرور با خروجی کلاینت متفاوت باشد، React خطای Hydration mismatch می‌دهد.

مثال مشکل

export default function Page() {
  return <p>{Date.now()}</p>;
}

چرا مشکل دارد؟

چون:

  • سرور یک timestamp می‌سازد
  • کلاینت timestamp دیگری

پس HTML متفاوت می‌شود.

راه حل

استفاده از Client Component

"use client";

import { useEffect, useState } from "react";

export default function Page() {
  const [time, setTime]
 = useState(0);

  useEffect(() => {
    setTime(Date.now());
  }, []
);

  return <p>{time}</p>;
}

Turbopack

Turbopack نسل جدید bundler در Next.js است که توسط Vercel ساخته شده.

جایگزین:

  • Webpack

ویژگی‌ها

  • نوشته شده با Rust
  • سرعت build بسیار بالا
  • incremental bundling
  • dev server بسیار سریع

فعال سازی:

next dev --turbo

next/font

در Next.js می‌توان فونت را self-hosted کرد.

بدون نیاز به Google CDN.

import { Inter } from "next/font/google";

const inter = Inter({ subsets: ["latin"]
 });

export default function Layout({ children }) {
  return <body className={inter.className}>{children}</body>;
}

مزایا:

  • zero layout shift
  • privacy بهتر
  • performance بهتر

Link Vs a tag

در Next.js باید از next/link استفاده کرد.

چرا؟

چون:

  • client-side navigation
  • prefetch
  • سریع‌تر
import Link from "next/link";

<Link href="/about">About</Link>;

خب Next.js لینک‌ها را قبل از کلیک preload می‌کند.

غیرفعال کردن:

<Link href="/about" prefetch={false}>

چرا Next.js سریع‌تر از CRA است؟

  • SSR / SSG
  • code splitting خودکار
  • image optimization
  • streaming
  • server components
  • turbopack

اگر صفحه‌ای داری که 90٪ static است ولی یک widget realtime دارد، چه می‌کنی؟

  • صفحه را static می‌سازم
  • و widget را client component می‌کنم
  • یا dynamic import
const ChatWidget = dynamic(() => import("./ChatWidget"), {
  ssr: false,
});