Next.js 15: فریمورک نهایی React - راهنمای کامل ۲۰۲۶

📊 آمارهای کلیدی Next.js در ۲۰۲۶:

  • 🌟 بیش از ۱۳۰ هزار ستاره در گیت‌هاب
  • 🏢 استفاده توسط غول‌های تکنولوژی: Netflix, TikTok, Twitch, Notion, Hulu
  • 📦 بیش از ۱۵ میلیون دانلود ماهانه از npm
  • ⚡ بهبود ۴۰٪ عملکرد در نسخه ۱۵ نسبت به نسخه ۱۴
  • 🔧 بیش از ۱۰۰ هزار وب‌سایت ساخته شده با Next.js
  • 📈 رشد ۳۰۰٪ استفاده در ۳ سال اخیر

🎯 خلاصه اجرایی:

Next.js قدرتمندترین فریمورک React برای تولید برنامه‌های وب مدرن است که توسط شرکت Vercel توسعه و نگهداری می‌شود. Next.js با ارائه قابلیت‌هایی مانند رندرینگ سمت سرور (SSR)، تولید سایت‌های ایستا (SSG)، بازتولید تدریجی (ISR)، مسیریابی خودکار و بهینه‌سازی تصاویر، تجربه توسعه‌دهندگان و عملکرد برنامه‌ها را به سطح جدیدی رسانده است. نسخه ۱۵ Next.js با معرفی App Router به عنوان پایدار، Turbopack سریع‌تر، و بهبودهای چشمگیر در Server Components، به انتخاب اول شرکت‌های بزرگ برای توسعه وب تبدیل شده است. در این مقاله از درخت کد، به بررسی عمیق این فریمورک قدرتمند می‌پردازیم.

مقدمه: چرا Next.js انقلابی در توسعه وب ایجاد کرد؟

قبل از Next.js، توسعه‌دهندگان React با چالش‌های بزرگی روبرو بودند: سئوی ضعیف (به دلیل رندرینگ سمت کلاینت)، زمان بارگذاری اولیه بالا، مسیریابی پیچیده و عدم وجود ساختار مشخص برای پروژه‌های بزرگ. Next.js توسط Guillermo Rauch (بنیانگذار Zeit، حالا Vercel) با یک ایده ساخته شد: "چارچوبی که تمام نیازهای یک پروژه React را در یکپکیج ارائه دهد."

یادم می‌آید اولین بار در سال ۲۰۱۸ وقتی Next.js 7 منتشر شد، در پروژه فروشگاه اینترنتی از آن استفاده کردم. تفاوت با Create React App مانند شب و روز بود. سئو به طور خودکار بهبود یافت، صفحه‌ها با سرعت باورنکردنی لود می‌شدند و تجربه توسعه‌دهندگی فوق‌العاده‌ای داشت. امروز پس از سال‌ها توسعه پروژه‌های بزرگ با Next.js و تدریس آن در درخت کد، می‌توانم بگویم Next.js استاندارد طلایی توسعه وب با React است.

۱.۱ تاریخچه نسخه‌های Next.js

۲۰۱۶: Next.js 1

اولین نسخه با SSR و مسیریابی خودکار

۲۰۱۷: Next.js 3

معرفی قابلیت Static Export

۲۰۱۸: Next.js 7

بهبود عملکرد و پشتیبانی از Webpack 4

۲۰۱۹: Next.js 9

معرفی SSG و API Routes

۲۰۲۰: Next.js 10

بهینه‌سازی تصاویر و i18n

۲۰۲۱: Next.js 12

معرفی Middleware و SWC

۲۰۲۲: Next.js 13

انقلاب App Router و Server Components (بتا)

۲۰۲۳: Next.js 14

بهبود Turbopack و Server Actions

۲۰۲۴-۲۰۲۶: Next.js 15

App Router پایدار، Turbopack سریع‌تر، بهبودهای عملکردی

بخش اول: مقایسه Next.js با سایر رویکردها

۱.۱ مقایسه با Create React App (SPA)

ویژگی Next.js Create React App
رندرینگ ✅ SSR, SSG, ISR, CSR ❌ فقط CSR
سئو (SEO) ✅ عالی ⚠️ ضعیف
زمان بارگذاری اولیه ✅ بسیار سریع ⚠️ کند
مسیریابی ✅ خودکار بر اساس فایل‌ها ❌ نیاز به React Router
API Routes ✅ داخلی ❌ نیاز به سرور جداگانه
بهینه‌سازی تصاویر ✅ داخلی ❌ نیاز به کتابخانه

۱.۲ مقایسه با سایر فریمورک‌های React

ویژگی Next.js Remix Gatsby
محبوبیت ⭐️⭐️⭐️⭐️⭐️ ⭐️⭐️⭐️ ⭐️⭐️⭐️
نوع رندرینگ SSR, SSG, ISR SSR SSG
منحنی یادگیری ✅ متوسط ⚠️ پیچیده ✅ آسان
اکوسیستم ⭐ بسیار بزرگ ⭐ متوسط ⭐ بزرگ
قابلیت مقیاس‌پذیری ✅ عالی ✅ خوب ⚠️ محدود

بخش دوم: معماری Next.js 15

۲.۱ App Router vs Pages Router

مقایسه دو رویکرد مسیریابی:

Pages Router (سنتی)                App Router (مدرن)
├── pages/                         ├── app/
│   ├── index.js                   │   ├── page.js
│   ├── about.js                   │   ├── about/
│   ├── products/                  │   │   └── page.js
│   │   ├── index.js               │   ├── products/
│   │   └── [id].js                │   │   ├── page.js
│   └── api/                       │   │   └── [id]/
│       └── hello.js               │   │       └── page.js
                                   │   └── api/
                                   │       └── hello/
                                   │           └── route.js

مزایای App Router:

  • ✅ پشتیبانی از React Server Components به صورت پیش‌فرض
  • ✅ مسیریابی تو در تو (Nested Routes) با layoutهای قابل استفاده مجدد
  • ✅ loading و error boundaries اختصاصی برای هر مسیر
  • ✅ پشتیبانی از موازی‌سازی (Parallel Routes) و اینترسپشن (Intercepting Routes)
  • ✅ بهبود عملکرد با streaming و partial rendering

بخش سوم: شروع کار با Next.js 15

۳.۱ نصب و راه‌اندازی

ایجاد پروژه جدید با Next.js 15:

# نصب با npm
npx create-next-app@latest my-app

# نصب با yarn
yarn create next-app my-app

# نصب با pnpm
pnpm create next-app my-app

# سوالات نصب
✔ Would you like to use TypeScript؟  Yes
✔ Would you like to use ESLint؟  Yes
✔ Would you like to use Tailwind CSS؟  Yes
✔ Would you like to use `src/` directory؟  Yes
✔ Would you like to use App Router؟  Yes
✔ Would you like to customize the default import alias؟  No

cd my-app
npm run dev

ساختار پروژه Next.js 15 با App Router:

my-app/
├── src/
│   ├── app/
│   │   ├── favicon.ico
│   │   ├── globals.css
│   │   ├── layout.js          # لایه اصلی
│   │   ├── page.js            # صفحه اصلی
│   │   ├── about/
│   │   │   └── page.js        # صفحه درباره ما
│   │   ├── products/
│   │   │   ├── page.js        # لیست محصولات
│   │   │   └── [id]/
│   │   │       └── page.js    # صفحه جزئیات محصول
│   │   └── api/
│   │       └── products/
│   │           └── route.js   # API محصولات
│   └── components/
│       ├── Header.jsx
│       └── Footer.jsx
├── public/
├── next.config.js
├── package.json
└── README.md

۳.۲ اولین صفحه با Next.js

src/app/page.js - صفحه اصلی:

import Link from 'next/link';

export default function Home() {
    return (
        <main className="flex min-h-screen flex-col items-center justify-center p-24">
            <h1 className="text-4xl font-bold mb-8">
                به Next.js 15 خوش آمدید!
            </h1>
            
            <div className="grid grid-cols-1 md:grid-cols-3 gap-8">
                <FeatureCard
                    title="رندرینگ پیشرفته"
                    description="SSR، SSG، ISR و CSR - هر چه نیاز دارید"
                />
                <FeatureCard
                    title="Server Components"
                    description="کامپوننت‌های سمت سرور با قابلیت‌های شگفت‌انگیز"
                />
                <FeatureCard
                    title="بهینه‌سازی خودکار"
                    description="تصاویر، فونت‌ها، اسکریپت‌ها - همه چیز بهینه می‌شود"
                />
            </div>
            
            <div className="mt-12">
                <Link 
                    href="/about" 
                    className="bg-blue-500 text-white px-6 py-3 rounded-lg hover:bg-blue-600"
                >
                    درباره ما
                </Link>
            </div>
        </main>
    );
}

function FeatureCard({ title, description }) {
    return (
        <div className="p-6 border rounded-lg shadow-sm">
            <h2 className="text-xl font-semibold mb-2">{title}</h2>
            <p className="text-gray-600">{description}</p>
        </div>
    );
}

src/app/layout.js - لایه اصلی:

import './globals.css';
import { Vazirmatn } from 'next/font/google';

const vazirmatn = Vazirmatn({ subsets: ['arabic'] });

export const metadata = {
    title: {
        default: 'درخت کد | آموزش Next.js',
        template: '%s | درخت کد'
    },
    description: 'آموزش کامل Next.js 15 - فریمورک نهایی React',
    keywords: ['Next.js', 'React', 'آموزش', 'برنامه‌نویسی'],
    authors: [{ name: 'محمدمهدی محمودی' }],
    openGraph: {
        title: 'آموزش Next.js 15',
        description: 'دوره کامل آموزش Next.js 15',
        siteName: 'درخت کد',
        locale: 'fa_IR',
        type: 'website',
    },
};

export default function RootLayout({ children }) {
    return (
        <html lang="fa" dir="rtl">
            <body className={vazirmatn.className}>
                <Header />
                <main>{children}</main>
                <Footer />
            </body>
        </html>
    );
}

بخش چهارم: روش‌های رندرینگ در Next.js 15

۴.۱ Server-Side Rendering (SSR)

رندرینگ سمت سرور با dynamic rendering:

// app/products/page.jsx
// این صفحه در هر درخواست، روی سرور رندر می‌شود

export const dynamic = 'force-dynamic';
// یا
export const revalidate = 0;

async function getProducts() {
    const res = await fetch('https://api.example.com/products', {
        cache: 'no-store' // غیرفعال کردن کش
    });
    
    if (!res.ok) {
        throw new Error('خطا در دریافت محصولات');
    }
    
    return res.json();
}

export default async function ProductsPage() {
    const products = await getProducts();
    
    return (
        <div>
            <h1>محصولات ({products.length})</h1>
            <div className="grid">
                {products.map(product => (
                    <div key={product.id} className="product-card">
                        <h3>{product.name}</h3>
                        <p>{product.price} تومان</p>
                    </div>
                ))}
            </div>
        </div>
    );
}

۴.۲ Static Site Generation (SSG)

تولید صفحات ایستا در زمان build:

// app/products/page.jsx
// این صفحه در زمان build تولید می‌شود

async function getProducts() {
    const res = await fetch('https://api.example.com/products', {
        cache: 'force-cache' // کش دائمی
    });
    
    return res.json();
}

export default async function ProductsPage() {
    const products = await getProducts();
    
    return (
        <div>
            <h1>محصولات</h1>
            {products.map(product => (
                <ProductCard key={product.id} product={product} />
            ))}
        </div>
    );
}

// app/products/[id]/page.jsx
// تولید صفحات داینامیک در زمان build

export async function generateStaticParams() {
    const products = await getProducts();
    
    return products.map(product => ({
        id: product.id.toString()
    }));
}

export default async function ProductPage({ params }) {
    const product = await getProduct(params.id);
    
    return (
        <div>
            <h1>{product.name}</h1>
            <p>{product.description}</p>
        </div>
    );
}

۴.۳ Incremental Static Regeneration (ISR)

بازتولید تدریجی صفحات:

// app/products/[id]/page.jsx
// این صفحه هر 60 ثانیه یک بار بازتولید می‌شود

export const revalidate = 60;

async function getProduct(id) {
    const res = await fetch(`https://api.example.com/products/${id}`, {
        next: {
            revalidate: 60 // بازتولید هر 60 ثانیه
        }
    });
    
    return res.json();
}

export default async function ProductPage({ params }) {
    const product = await getProduct(params.id);
    
    return (
        <div>
            <h1>{product.name}</h1>
            <p>آخرین به‌روزرسانی: {new Date().toLocaleString('fa-IR')}</p>
            <p>{product.description}</p>
        </div>
    );
}

۴.۴ Client-Side Rendering (CSR)

رندرینگ سمت کلاینت با 'use client':

// app/dashboard/page.jsx
'use client';

import { useState, useEffect } from 'react';

export default function Dashboard() {
    const [data, setData] = useState(null);
    const [loading, setLoading] = useState(true);
    
    useEffect(() => {
        fetch('/api/dashboard')
            .then(res => res.json())
            .then(data => {
                setData(data);
                setLoading(false);
            });
    }, []);
    
    if (loading) return <div>در حال بارگذاری...</div>;
    
    return (
        <div>
            <h1>داشبورد</h1>
            <p>تعداد کاربران: {data.users}</p>
            <p>فروش امروز: {data.sales}</p>
        </div>
    );
}

بخش پنجم: Server Components vs Client Components

۵.۱ تفاوت‌های کلیدی

ویژگی Server Components Client Components
محل اجرا ✅ سرور ✅ کلاینت
دسترسی به دیتابیس ✅ مستقیم ❌ از طریق API
Hooks (useState, useEffect) ❌ ندارد ✅ دارد
تعامل کاربر ❌ ندارد ✅ دارد
حجم جاوااسکریپت ✅ صفر ⚠️ دارد
سئو ✅ عالی ⚠️ وابسته به SSR

ترکیب Server و Client Components:

// app/products/page.jsx - Server Component (پیش‌فرض)
import ProductList from '@/components/ProductList';
import { getProducts } from '@/lib/products';

export default async function ProductsPage() {
    const products = await getProducts();
    
    return (
        <div>
            <h1>محصولات ما</h1>
            <ProductList initialProducts={products} />
        </div>
    );
}

// components/ProductList.jsx - Client Component
'use client';

import { useState } from 'react';

export default function ProductList({ initialProducts }) {
    const [products, setProducts] = useState(initialProducts);
    const [filter, setFilter] = useState('');
    
    const filteredProducts = products.filter(p => 
        p.name.includes(filter)
    );
    
    return (
        <div>
            <input
                type="text"
                value={filter}
                onChange={(e) => setFilter(e.target.value)}
                placeholder="جستجوی محصول..."
            />
            
            <div className="grid">
                {filteredProducts.map(product => (
                    <ProductCard key={product.id} product={product} />
                ))}
            </div>
        </div>
    );
}

بخش ششم: پروژه‌های عملی با Next.js

۶.۱ نمونه ۱: وبلاگ با Next.js

سیستم وبلاگ با Markdown:

// lib/posts.js
import fs from 'fs';
import path from 'path';
import matter from 'gray-matter';

const postsDirectory = path.join(process.cwd(), 'posts');

export async function getPosts() {
    const fileNames = fs.readdirSync(postsDirectory);
    
    const posts = fileNames.map(fileName => {
        const slug = fileName.replace(/\.md$/, '');
        const fullPath = path.join(postsDirectory, fileName);
        const fileContents = fs.readFileSync(fullPath, 'utf8');
        const { data, content } = matter(fileContents);
        
        return {
            slug,
            title: data.title,
            date: data.date,
            excerpt: data.excerpt,
            content
        };
    });
    
    return posts.sort((a, b) => (a.date < b.date ? 1 : -1));
}

export async function getPost(slug) {
    const fullPath = path.join(postsDirectory, `${slug}.md`);
    const fileContents = fs.readFileSync(fullPath, 'utf8');
    const { data, content } = matter(fileContents);
    
    return {
        slug,
        title: data.title,
        date: data.date,
        excerpt: data.excerpt,
        content
    };
}

// app/blog/page.jsx
import Link from 'next/link';
import { getPosts } from '@/lib/posts';

export default async function BlogPage() {
    const posts = await getPosts();
    
    return (
        <div className="container mx-auto px-4">
            <h1 className="text-3xl font-bold mb-8">وبلاگ درخت کد</h1>
            
            <div className="grid gap-8">
                {posts.map(post => (
                    <article key={post.slug} className="border rounded-lg p-6">
                        <h2 className="text-xl font-semibold mb-2">
                            <Link href={`/blog/${post.slug}`}>
                                {post.title}
                            </Link>
                        </h2>
                        <p className="text-gray-600 mb-2">
                            {new Date(post.date).toLocaleDateString('fa-IR')}
                        </p>
                        <p className="mb-4">{post.excerpt}</p>
                        <Link 
                            href={`/blog/${post.slug}`}
                            className="text-blue-500 hover:underline"
                        >
                            ادامه مطلب
                        </Link>
                    </article>
                ))}
            </div>
        </div>
    );
}

// app/blog/[slug]/page.jsx
import { getPost, getPosts } from '@/lib/posts';
import { notFound } from 'next/navigation';
import Markdown from 'react-markdown';

export async function generateStaticParams() {
    const posts = await getPosts();
    return posts.map(post => ({ slug: post.slug }));
}

export default async function BlogPostPage({ params }) {
    const post = await getPost(params.slug);
    
    if (!post) {
        notFound();
    }
    
    return (
        <article className="container mx-auto px-4 py-8">
            <h1 className="text-3xl font-bold mb-4">{post.title}</h1>
            <p className="text-gray-600 mb-8">
                {new Date(post.date).toLocaleDateString('fa-IR')}
            </p>
            <div className="prose prose-lg">
                <Markdown>{post.content}</Markdown>
            </div>
        </article>
    );
}

۶.۲ نمونه ۲: فروشگاه اینترنتی با Next.js

سیستم سبد خرید و محصولات:

// app/products/page.jsx
import { getProducts } from '@/lib/products';
import ProductGrid from '@/components/ProductGrid';

export const metadata = {
    title: 'محصولات | فروشگاه درخت کد',
    description: 'لیست تمام محصولات فروشگاه',
};

export default async function ProductsPage() {
    const products = await getProducts();
    
    return (
        <div className="container mx-auto px-4">
            <h1 className="text-3xl font-bold mb-8">محصولات</h1>
            <ProductGrid products={products} />
        </div>
    );
}

// components/ProductGrid.jsx
'use client';

import { useState } from 'react';
import { useCart } from '@/context/CartContext';
import Image from 'next/image';

export default function ProductGrid({ products }) {
    const [filteredProducts, setFilteredProducts] = useState(products);
    const [category, setCategory] = useState('all');
    const { addToCart } = useCart();
    
    const categories = [...new Set(products.map(p => p.category))];
    
    const filterByCategory = (cat) => {
        setCategory(cat);
        if (cat === 'all') {
            setFilteredProducts(products);
        } else {
            setFilteredProducts(products.filter(p => p.category === cat));
        }
    };
    
    return (
        <div>
            <div className="flex gap-2 mb-6">
                <button
                    onClick={() => filterByCategory('all')}
                    className={`px-4 py-2 rounded ${
                        category === 'all' ? 'bg-blue-500 text-white' : 'bg-gray-200'
                    }`}
                >
                    همه
                </button>
                {categories.map(cat => (
                    <button
                        key={cat}
                        onClick={() => filterByCategory(cat)}
                        className={`px-4 py-2 rounded ${
                            category === cat ? 'bg-blue-500 text-white' : 'bg-gray-200'
                        }`}
                    >
                        {cat}
                    </button>
                ))}
            </div>
            
            <div className="grid grid-cols-1 md:grid-cols-3 lg:grid-cols-4 gap-6">
                {filteredProducts.map(product => (
                    <div key={product.id} className="border rounded-lg p-4">
                        <Image
                            src={product.image}
                            alt={product.name}
                            width={200}
                            height={200}
                            className="w-full h-48 object-cover mb-4"
                        />
                        <h3 className="font-semibold mb-2">{product.name}</h3>
                        <p className="text-gray-600 mb-2">{product.price} تومان</p>
                        <button
                            onClick={() => addToCart(product)}
                            className="w-full bg-blue-500 text-white px-4 py-2 rounded hover:bg-blue-600"
                        >
                            افزودن به سبد خرید
                        </button>
                    </div>
                ))}
            </div>
        </div>
    );
}

// context/CartContext.jsx
'use client';

import { createContext, useContext, useState } from 'react';

const CartContext = createContext();

export function CartProvider({ children }) {
    const [cart, setCart] = useState([]);
    
    const addToCart = (product) => {
        setCart(prev => {
            const existing = prev.find(item => item.id === product.id);
            if (existing) {
                return prev.map(item =>
                    item.id === product.id
                        ? { ...item, quantity: item.quantity + 1 }
                        : item
                );
            }
            return [...prev, { ...product, quantity: 1 }];
        });
    };
    
    const removeFromCart = (id) => {
        setCart(prev => prev.filter(item => item.id !== id));
    };
    
    const updateQuantity = (id, quantity) => {
        setCart(prev =>
            prev.map(item =>
                item.id === id ? { ...item, quantity } : item
            )
        );
    };
    
    const total = cart.reduce((sum, item) => sum + item.price * item.quantity, 0);
    const itemCount = cart.reduce((sum, item) => sum + item.quantity, 0);
    
    return (
        <CartContext.Provider value={{
            cart,
            addToCart,
            removeFromCart,
            updateQuantity,
            total,
            itemCount
        }}>
            {children}
        </CartContext.Provider>
    );
}

export function useCart() {
    const context = useContext(CartContext);
    if (!context) {
        throw new Error('useCart must be used within CartProvider');
    }
    return context;
}

بخش هفتم: API Routes و Server Actions

۷.۱ API Routes در Next.js

ایجاد API در App Router:

// app/api/products/route.js
import { getProducts, createProduct } from '@/lib/products';
import { NextResponse } from 'next/server';

export async function GET() {
    try {
        const products = await getProducts();
        return NextResponse.json(products);
    } catch (error) {
        return NextResponse.json(
            { error: 'خطا در دریافت محصولات' },
            { status: 500 }
        );
    }
}

export async function POST(request) {
    try {
        const body = await request.json();
        const newProduct = await createProduct(body);
        return NextResponse.json(newProduct, { status: 201 });
    } catch (error) {
        return NextResponse.json(
            { error: 'خطا در ایجاد محصول' },
            { status: 500 }
        );
    }
}

// app/api/products/[id]/route.js
export async function GET(request, { params }) {
    const product = await getProduct(params.id);
    
    if (!product) {
        return NextResponse.json(
            { error: 'محصول یافت نشد' },
            { status: 404 }
        );
    }
    
    return NextResponse.json(product);
}

۷.۲ Server Actions در Next.js 15

مدیریت فرم با Server Actions:

// app/contact/page.jsx
'use server';

import { revalidatePath } from 'next/cache';
import { redirect } from 'next/navigation';

async function submitContact(formData) {
    'use server';
    
    const name = formData.get('name');
    const email = formData.get('email');
    const message = formData.get('message');
    
    // اعتبارسنجی
    if (!name || !email || !message) {
        return {
            error: 'همه فیلدها الزامی هستند'
        };
    }
    
    // ذخیره در دیتابیس
    await saveContact({ name, email, message });
    
    // ارسال ایمیل
    await sendEmail({ name, email, message });
    
    // بازتولید کش
    revalidatePath('/contact');
    
    // هدایت
    redirect('/contact/success');
}

export default function ContactPage() {
    return (
        <form action={submitContact} className="max-w-md mx-auto">
            <div className="mb-4">
                <label className="block mb-2">نام:</label>
                <input
                    type="text"
                    name="name"
                    className="w-full border rounded p-2"
                    required
                />
            </div>
            
            <div className="mb-4">
                <label className="block mb-2">ایمیل:</label>
                <input
                    type="email"
                    name="email"
                    className="w-full border rounded p-2"
                    required
                />
            </div>
            
            <div className="mb-4">
                <label className="block mb-2">پیام:</label>
                <textarea
                    name="message"
                    rows="4"
                    className="w-full border rounded p-2"
                    required
                ></textarea>
            </div>
            
            <button
                type="submit"
                className="bg-blue-500 text-white px-6 py-2 rounded hover:bg-blue-600"
            >
                ارسال پیام
            </button>
        </form>
    );
}

بخش هشتم: بهینه‌سازی و سئو در Next.js

۸.۱ بهینه‌سازی تصاویر با next/image

استفاده از Image Component:

import Image from 'next/image';

export default function OptimizedImage() {
    return (
        <div>
            <!-- تصویر محلی -->
            <Image
                src="/profile.jpg"
                alt="پروفایل"
                width={400}
                height={400}
                className="rounded-full"
                priority // برای تصاویر بالای صفحه
            />
            
            <!-- تصویر از راه دور -->
            <Image
                src="https://example.com/image.jpg"
                alt="تصویر از راه دور"
                width={800}
                height={600}
                quality={90} // کیفیت تصویر
                placeholder="blur" // نمایش بلور در حال لود
                blurDataURL="data:image/jpeg;base64,..." // تصویر بلور
            />
            
            <!-- تصویر واکنش‌گرا -->
            <Image
                src="/hero.jpg"
                alt="hero"
                fill
                className="object-cover"
                sizes="(max-width: 768px) 100vw, 50vw"
            />
        </div>
    );
}

۸.۲ بهینه‌سازی فونت با next/font

استفاده از فونت‌های بهینه:

// app/layout.js
import { Vazirmatn } from 'next/font/google';

const vazirmatn = Vazirmatn({
    subsets: ['arabic'],
    display: 'swap', // بهبود عملکرد
    variable: '--font-vazirmatn', // متغیر CSS
    weight: ['400', '500', '600', '700'], // وزن‌های مورد نیاز
});

export default function RootLayout({ children }) {
    return (
        <html lang="fa" dir="rtl" className={vazirmatn.variable}>
            <body style={{ fontFamily: 'var(--font-vazirmatn)' }}>
                {children}
            </body>
        </html>
    );
}

۸.۳ سئو با Metadata API

مدیریت متادیتا برای سئو:

// app/products/[id]/page.jsx
import { getProduct } from '@/lib/products';

export async function generateMetadata({ params }) {
    const product = await getProduct(params.id);
    
    return {
        title: product.name,
        description: product.description,
        keywords: product.tags,
        openGraph: {
            title: product.name,
            description: product.description,
            images: [
                {
                    url: product.image,
                    width: 800,
                    height: 600,
                    alt: product.name,
                },
            ],
        },
        twitter: {
            card: 'summary_large_image',
            title: product.name,
            description: product.description,
            images: [product.image],
        },
        alternates: {
            canonical: `https://treec.net/products/${params.id}`,
        },
    };
}

export default function ProductPage({ params }) {
    // ...
}

بخش نهم: Middleware و احراز هویت

Middleware برای محافظت از مسیرها:

// middleware.js
import { NextResponse } from 'next/server';
import { verifyToken } from '@/lib/auth';

export function middleware(request) {
    const token = request.cookies.get('token')?.value;
    const { pathname } = request.nextUrl;
    
    // مسیرهای عمومی
    const publicPaths = ['/', '/about', '/products', '/login', '/register'];
    const isPublicPath = publicPaths.some(path => 
        pathname.startsWith(path)
    );
    
    // مسیرهای محافظت شده
    const protectedPaths = ['/dashboard', '/profile', '/admin'];
    const isProtectedPath = protectedPaths.some(path => 
        pathname.startsWith(path)
    );
    
    // اگر مسیر محافظت شده است و توکن وجود ندارد
    if (isProtectedPath && !token) {
        const url = new URL('/login', request.url);
        url.searchParams.set('redirect', pathname);
        return NextResponse.redirect(url);
    }
    
    // اگر کاربر وارد شده و به صفحه لاگین می‌رود
    if (pathname === '/login' && token) {
        return NextResponse.redirect(new URL('/dashboard', request.url));
    }
    
    return NextResponse.next();
}

export const config = {
    matcher: [
        '/*',
        '/dashboard/:path*',
        '/profile/:path*',
        '/admin/:path*',
    ],
};

بخش دهم: دیپلوی و استقرار

۱۰.۱ دیپلوی روی Vercel

استقرار در Vercel (ساده‌ترین روش):

# نصب Vercel CLI
npm i -g vercel

# دیپلوی پروژه
vercel

# دیپلوی برای production
vercel --prod

# یا اتصال به GitHub و دیپلوی خودکار
1. push کد به GitHub
2. import پروژه در vercel.com
3. دیپلوی خودکار با هر push

۱۰.۲ متغیرهای محیطی

مدیریت environment variables:

// .env.local (توسعه محلی)
DATABASE_URL="mysql://localhost:3306/mydb"
API_KEY="your-api-key"
NEXT_PUBLIC_GA_ID="G-XXXXXXXX" // با NEXT_PUBLIC_ در کلاینت هم قابل دسترس است

// .env.production (محصول)
DATABASE_URL="mysql://production:password@prod-db:3306/proddb"
API_KEY="prod-api-key"

// استفاده در کد
const dbUrl = process.env.DATABASE_URL;
const gaId = process.env.NEXT_PUBLIC_GA_ID;

بخش یازدهم: بازار کار و فرصت‌های شغلی

تحلیل بازار کار ایران در ۲۰۲۶:

موقعیت شغلی متوسط حقوق (تومان) تقاضا مهارت‌های مورد نیاز
توسعه‌دهنده Next.js ارشد ۷۰-۱۰۰ میلیون ⭐️⭐️⭐️⭐️⭐️ Next.js 15، React 19، TypeScript، Tailwind
توسعه‌دهنده Full-Stack ۸۰-۱۲۰ میلیون ⭐️⭐️⭐️⭐️⭐️ Next.js، Node.js، MongoDB، Prisma
توسعه‌دهنده فرانت‌اند ۵۰-۸۰ میلیون ⭐️⭐️⭐️⭐️⭐️ Next.js، React، TypeScript، REST API
معمار وب ۱۰۰-۱۵۰ میلیون ⭐️⭐️⭐️⭐️ Next.js، Microservices، DevOps، Cloud

فرصت‌های شغلی بین‌المللی:

  • Next.js Developer: $70,000 - $140,000 سالانه
  • Senior Full-Stack (Next.js): $90,000 - $170,000 سالانه
  • Frontend Architect: $120,000 - $200,000 سالانه
  • Remote Next.js Developer: $60,000 - $130,000 سالانه

نتیجه‌گیری: چرا Next.js؟

Next.js ۱۵ نه فقط یک فریمورک React، بلکه یک پلتفرم کامل برای توسعه وب مدرن است. با ترکیب قدرت React، بهینه‌سازی‌های خودکار، و قابلیت‌های بی‌نظیر سمت سرور، Next.js به استاندارد طلایی صنعت تبدیل شده است.

✅ برای مبتدیان:

  • شروع آسان با create-next-app
  • مستندات عالی و کامل
  • جامعه بزرگ و فعال
  • منابع آموزشی فراوان

💼 برای کسب‌وکارها:

  • سئوی عالی برای دیده شدن
  • عملکرد فوق‌العاده و سرعت بالا
  • مقیاس‌پذیری بی‌نظیر
  • کاهش هزینه‌های زیرساخت

🚀 برای توسعه‌دهندگان:

  • بازار کار داغ و پرتقاضا
  • حقوق‌های بالا در ایران و جهان
  • تجربه توسعه لذت‌بخش
  • یادگیری تکنولوژی‌های روز

در درخت کد، ما Next.js را به عنوان اصلی‌ترین فریمورک برای پروژه‌های React خود انتخاب کرده‌ایم. ما همراه شما هستیم تا با آموزش‌های تخصصی، پروژه‌های عملی و مشاوره‌های فنی، مسیر تبدیل شدن به یک توسعه‌دهنده حرفه‌ای Next.js را هموار کنیم.

🎯 راهنمای عملی شروع با Next.js 15:

  1. مطالعه مستندات رسمی: Next.js Documentation
  2. شرکت در دوره Next.js درخت کد
  3. ساخت یک پروژه عملی (وبلاگ، فروشگاه، داشبورد)
  4. یادگیری TypeScript و Tailwind CSS
  5. دیپلوی روی Vercel و انتشار پروژه

با آرزوی موفقیت در مسیر توسعه وب،

محمدمهدی محمودی
بنیانگذار و مدرس ارشد درخت کد
treec.net | mohammadmahdimahmoudi.ir