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