Mojtaba Pourkhanlar
About meProjectsBlog

  • 👤About me
  • 🧰Projects
  • ✍️Blog

Next.js In-depth Review


Data Fetching (Pages Router)

getServerSideProps

  • برای گرفتن داده در هر درخواست از سرور (SSR).
export async function getServerSideProps() {
  const res = await fetch("https://api.example.com/data");
  const data = await res.json();

  return {
    props: { data },
  };
}

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

// معادلش در Next.js 15:
fetch(..., { cache: "no-store" }) // داخل Server Component
  • مناسب صفحه‌های داینامیک و real-time
  • همیشه روی سرور اجرا میشه

getStaticProps

  • صفحه یک بار موقع بیلد ساخته میشه و استاتیک میشه
export async function getStaticProps() {
  const res = await fetch("https://api.example.com/data");
  const data = await res.json();

  return {
    props: { data },
  };
}

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

// معادلش در Next.js 15:
export const dynamic = "force-static";
  • فقط یکبار اجرا میشه
  • خروجی HTML ثابت ساخته میشه
  • فوق‌العاده سریع
  • بهترین انتخاب برای وبلاگ‌ها و صفحات بدون تغییر

getStaticPaths

  • برای ساخت صفحات داینامیک استاتیک (SSG) با مسیرهای از پیش مشخص.
export async function getStaticPaths() {
  const res = await fetch("https://api.example.com/posts");
  const posts = await res.json();

  const paths = posts.map(post => ({
    params: { id: post.id.toString() },
  }));

  return {
    paths,
    fallback: false,
  };
}

export async function getStaticProps({ params }) {
  const res = await fetch(`https://api.example.com/posts/${params.id}`);
  const post = await res.json();

  return {
    props: { post },
  };
}

// معادلش در Next.js 15:
generateStaticParams();
  • میگه کدوم صفحه‌ها باید قبل از build ساخته بشن
  • برای routes داینامیک ضروریه
  • همراه با fallback می‌تونه ISR هم ایجاد کنه

Next.js 13+

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 بهترین انتخاب برای بلاگ‌ها، لندینگ‌ها و محتواهای نیمه‌داینامیک است.

App Router

معماری جدید مسیریابی بر پایه پوشه app/ که جایگزین pages/ شد.

app/
 ├─ layout.tsx
 ├─ page.tsx        → /
 └─ blog/
     └─ page.tsx    → /blog
export default function Page() {
  return <h1>Hello App Router</h1>;
}

در Next.js 13+ می‌توان layout مشترک ساخت.

app/layout.tsx
export default function RootLayout({ children }) {
  return (
    <html>
      <body>
        <nav>Navbar</nav>
        {children}
      </body>
    </html>
  );
}

Streaming با Suspense

خب Next.js می‌تواند صفحه را بخش بخش ارسال کند. با React Suspense

Flow:

1 Header سریع میاد
2 fallback میاد
3 Products بعداً stream میشه

یعنی کاربر صفحه سفید نمیبینه

import { Suspense } from "react";
import Posts from "./Posts";

export default function Page() {
  return (
    <div>
      <h1>Dashboard</h1>

      <Suspense fallback={<p>Loading...</p>}>
        <Posts />
      </Suspense>
    </div>
  );
}
  • سریع‌تر

  • تجربه کاربری بهتر

  • مناسب صفحات سنگین

API Routes

  • امکان ساخت endpointهای API داخلی داخل پروژه را فراهم می‌کند.
// pages/api/hello.js
export default function handler(req, res) {
  res.status(200).json({ message: "Hello API" });
}

Image Optimization

  • با next/image تصاویر به صورت اتوماتیک بهینه‌سازی می‌شوند (lazy loading, resize, format).
import Image from "next/image";

export default function Home() {
  return <Image src="/me.png" alt="me" width={300} height={300} />;
}

Middleware

خب middleware قبل از رسیدن request به route اجرا می‌شود.

کاربردها:

  • authentication
  • redirect
  • logging
  • rate limiting
import { NextResponse } from "next/server";

export function middleware(req) {
  const isAuth = false;

  if (!isAuth) {
    return NextResponse.redirect(new URL("/login", req.url));
  }
}

Dynamic Routing

ساخت route داینامیک.

app/blog/[slug]
/page.tsx
export default function Page({ params }) {
  return <h1>{params.slug}</h1>;
}
/blog/nextjs

Parallel Routes

قابلیت جدید App Router.

اجازه می‌دهد چند route همزمان رندر شوند.

ساختار:

app/@analytics/page.tsx
app/@team/page.tsx

در layout:

export default function Layout({ analytics, team }) {
  return (
    <>
      {analytics}
      {team}
    </>
  );
}

Intercepting Routes + Parallel Routes (Modal Routing)

اجازه می‌دهد route را داخل route دیگر باز کنیم.

مثلاً:

  • مثلا کاربر سبد خریدش را میخواهد ثبت کند تا تکمیل سفارش انجام شود
  • در این مرحله به جایه اینکه کاربر به صفحه /auth هدایت شود یک مودال باز میشود با مسیر /auth و ....
app/
  layout.tsx
  auth/page.tsx

  @modal/
    (.)auth/page.tsx
  • حالا تغییرات در فایل app/Layout.tsx
export default function Layout({
  children,
  modal,
}: Readonly<{
  children: React.ReactNode;
  modal: React.ReactNode;
}>) {
  return (
    <html lang="en">
      <body>
        {children}
        {modal}
      </body>
    </html>
  );
}

Revalidation Tag

در Next 13+ می‌توان cache را با tag invalidate کرد.

// fetch با tag
fetch(url, {
  next: { tags: ["posts"]
 },
});

// invalidate
import { revalidateTag } from "next/cache";

revalidateTag("posts");

وقتی داده عوض شود cache آپدیت می‌شود.


Server Actions

  • ارسال فرم و اجرای منطق بک‌اند بدون API Route.
  • حذف API Routes
  • حذف fetch در فرم‌ها
  • ساده‌سازی Full-stack
"use server";

export async function createPost(formData: FormData) {
  const title = formData.get("title");
}
<form action={createPost}>
  <input name="title" />
  <button>Submit</button>
</form>

نکات مهم

  • فقط روی سرور
  • قابل استفاده با redirect و revalidate

بهبود Caching و Revalidation

کنترل دقیق روی کش داده‌ها.

fetch(url, { next: { revalidate: 60 } });

اگر صفحه:

سناریو پیشنهاد
فقط نمایش دیتا Server Component
تعامل کاربر Client Component
فرم Server Action
عمومی Static / ISR
شخصی SSR / PPR

اشتباهات رایج

  • استفاده بیش از حد از "use client"
  • و Fetch در Client بدون نیاز
  • همینطور State در Layout وقتی Template لازم است
  • نادیده گرفتن Cache و Revalidation