MATIN MOLKARA
SERIES: آموزش Next js — PART 3 OF 28

قسمت سوم - مسیریابی، Layoutها و Navigation در Next.js 16

در Next.js، مسیرها به طور پیش‌فرض روی سرور رندر می‌شوند. این اغلب به این معنی است که کلاینت باید منتظر پاسخ سرور بماند تا مسیر جدیدی نمایش داده شود. Next.js دارای پیش‌واکشی (prefetching) که تضمین می‌کند Navigation سریع و پاسخگو باقی بماند.

مسیریابی، Layoutها و Navigation در Next.js 16

در پست قبل، ساختار کلی پروژه را برای یک وب‌سایت پرتفولیو، بلاگ و داشبورد بررسی کردیم. حالا وقت آن است که وارد یکی از مهم‌ترین بخش‌های Next.js شویم: مسیریابی یا Routing.

در Next.js 16، وقتی از App Router استفاده می‌کنیم، مسیرهای سایت بر اساس ساختار فایل‌ها و پوشه‌های داخل پوشه app/ ساخته می‌شوند. یعنی برای ساختن صفحه‌ها، دیگر نیازی نیست routeها را به‌صورت دستی در یک فایل جداگانه تعریف کنیم. خود Next.js از روی پوشه‌ها و فایل‌های خاص مثل page.tsx و layout.tsx مسیرها را تشخیص می‌دهد.

در این پست یاد می‌گیریم:

  • page.tsx چیست و چطور یک صفحه می‌سازد
  • layout.tsx چه نقشی دارد
  • چطور routeهای تو در تو بسازیم
  • dynamic route چیست و برای بلاگ و پروژه‌ها چه کاربردی دارد
  • params و searchParams در Next.js 16 چطور کار می‌کنند
  • چطور با Link بین صفحه‌ها حرکت کنیم
  • چرا loading.tsx برای تجربه کاربری بهتر مهم است

مسیریابی فایل‌محور در Next.js

Next.js از file-system based routing استفاده می‌کند. یعنی پوشه‌ها و فایل‌ها مسیرهای سایت را می‌سازند.

به زبان ساده:

  • پوشه‌ها segmentهای URL را می‌سازند
  • فایل‌های خاص مثل page.tsx رابط کاربری آن مسیر را می‌سازند
  • فایل‌های خاص مثل layout.tsx ساختار مشترک بین چند صفحه را تعریف می‌کنند

مثلا اگر چنین فایلی داشته باشیم:

app/page.tsx

این فایل صفحه اصلی سایت را می‌سازد:

/

اگر چنین فایلی بسازیم:

app/blog/page.tsx

مسیر زیر ساخته می‌شود:

/blog

و اگر چنین ساختاری داشته باشیم:

app/blog/[slug]/page.tsx

یک مسیر داینامیک برای پست‌های بلاگ ساخته می‌شود:

/blog/my-first-post
/blog/nextjs-routing
/blog/prisma-guide

ساخت اولین صفحه با page.tsx

در App Router، برای ساخت یک صفحه باید داخل پوشه موردنظر یک فایل page.tsx بسازیم و از آن یک React Component را به‌صورت default export خروجی بدهیم.

مثلا صفحه اصلی پروژه:

// app/page.tsx
export default function HomePage() {
  return (
    <main>
      <h1>Matin Portfolio</h1>
      <p>Personal portfolio, blog, and dashboard built with Next.js.</p>
    </main>
  )
}

این فایل مسیر اصلی سایت را می‌سازد:

/

نکته مهم این است که در App Router، کامپوننت‌های داخل app/ به‌صورت پیش‌فرض Server Component هستند. یعنی تا وقتی از use client استفاده نکنیم، این کامپوننت در سمت سرور render می‌شود.

این رفتار برای پروژه ما خیلی مفید است، چون صفحات عمومی مثل صفحه اصلی، بلاگ و پروژه‌ها می‌توانند سریع‌تر و سبک‌تر باشند.


ساخت routeهای اصلی پروژه

برای پروژه پرتفولیو، در شروع می‌توانیم چند route اصلی داشته باشیم:

app/
├── page.tsx
├── about/
│   └── page.tsx
├── contact/
│   └── page.tsx
├── blog/
│   └── page.tsx
├── projects/
│   └── page.tsx
├── login/
│   └── page.tsx
└── dashboard/
    └── page.tsx

خروجی این ساختار چنین مسیرهایی است:

/
 /about
/contact
/blog
/projects
/login
/dashboard

البته مسیر اول باید بدون فاصله نوشته شود:

/

نمونه صفحه درباره من

برای ساخت صفحه درباره من:

// app/about/page.tsx
export default function AboutPage() {
  return (
    <main>
      <h1>About Me</h1>
      <p>
        I am a developer building modern web applications with Next.js,
        React, Prisma, and Tailwind CSS.
      </p>
    </main>
  )
}

این صفحه از طریق مسیر زیر در دسترس است:

/about

نمونه صفحه تماس

برای صفحه تماس:

// app/contact/page.tsx
export default function ContactPage() {
  return (
    <main>
      <h1>Contact</h1>
      <p>You can reach me through email or social links.</p>
    </main>
  )
}

این فایل مسیر زیر را می‌سازد:

/contact

Layout چیست؟

در Next.js، layout بخشی از UI است که بین چند صفحه مشترک می‌ماند.

مثلا در بیشتر سایت‌ها این بخش‌ها در همه صفحه‌ها تکرار می‌شوند:

  • Navbar
  • Footer
  • ساختار کلی صفحه
  • Theme wrapper
  • Providerها
  • Sidebar داشبورد

در App Router، برای ساخت layout باید فایل layout.tsx بسازیم.

مهم‌ترین layout پروژه، Root Layout است.


Root Layout در Next.js 16

Root Layout فایلی است که در ریشه پوشه app/ قرار می‌گیرد:

app/layout.tsx

این فایل در Next.js الزامی است و باید شامل تگ‌های html و body باشد.

نمونه ساده:

// app/layout.tsx
import type { Metadata } from 'next'
import './globals.css'

export const metadata: Metadata = {
  title: 'Matin Portfolio',
  description: 'Personal portfolio, blog, and dashboard built with Next.js',
}

export default function RootLayout({
  children,
}: {
  children: React.ReactNode
}) {
  return (
    <html lang="en">
      <body>
        {children}
      </body>
    </html>
  )
}

هر صفحه‌ای که داخل app/ ساخته شود، در نهایت داخل همین children قرار می‌گیرد.

یعنی اگر کاربر وارد /blog شود، محتوای app/blog/page.tsx داخل children این layout نمایش داده می‌شود.


اضافه کردن Navbar و Footer به Root Layout

برای پروژه ما، بهتر است Navbar و Footer عمومی در Root Layout قرار بگیرند تا در صفحه‌های عمومی سایت تکرار شوند.

مثلا:

// app/layout.tsx
import type { Metadata } from 'next'
import Link from 'next/link'
import './globals.css'

export const metadata: Metadata = {
  title: 'Matin Portfolio',
  description: 'Portfolio, blog, and dashboard',
}

export default function RootLayout({
  children,
}: {
  children: React.ReactNode
}) {
  return (
    <html lang="en">
      <body>
        <header>
          <nav>
            <Link href="/">Home</Link>
            <Link href="/about">About</Link>
            <Link href="/projects">Projects</Link>
            <Link href="/blog">Blog</Link>
            <Link href="/contact">Contact</Link>
          </nav>
        </header>

        {children}

        <footer>
          <p>© Matin</p>
        </footer>
      </body>
    </html>
  )
}

در پروژه واقعی، بهتر است Navbar و Footer را در پوشه components/layout/ جدا کنیم، اما برای شروع این مثال ساده‌تر است.


چرا باید از Link استفاده کنیم؟

در Next.js برای جابه‌جایی بین صفحه‌ها، روش اصلی استفاده از کامپوننت Link است:

import Link from 'next/link'

مثلا:

<Link href="/blog">Blog</Link>

استفاده از Link مزایای مهمی دارد:

  • navigation سمت کلاینت انجام می‌شود
  • صفحه کامل reload نمی‌شود
  • layoutهای مشترک حفظ می‌شوند
  • Next.js می‌تواند routeها را prefetch کند
  • تجربه حرکت بین صفحه‌ها سریع‌تر می‌شود

اگر از تگ معمولی a استفاده کنیم:

<a href="/blog">Blog</a>

مرورگر معمولا صفحه را مثل یک سایت سنتی بارگذاری می‌کند. اما Link برای routing داخلی Next.js بهینه شده است.

پس قانون ساده این است:

برای لینک‌های داخلی سایت، از Link استفاده می‌کنیم. برای لینک‌های خارجی، می‌توانیم از a استفاده کنیم.


Prefetching در Next.js 16

یکی از قابلیت‌های مهم Link در Next.js، prefetching است.

یعنی وقتی یک لینک وارد viewport کاربر می‌شود یا احتمال دارد کاربر روی آن کلیک کند، Next.js می‌تواند اطلاعات آن route را از قبل در پس‌زمینه آماده کند.

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

مثلا:

<Link href="/projects">Projects</Link>

این لینک می‌تواند مسیر /projects را از قبل آماده کند.

اما میزان prefetch شدن به نوع route بستگی دارد:

  • routeهای static معمولا کامل prefetch می‌شوند
  • routeهای dynamic ممکن است prefetch نشوند یا فقط بخشی از آن‌ها prefetch شود
  • اگر route داینامیک loading.tsx داشته باشد، Next.js می‌تواند fallback و layoutهای مشترک را زودتر آماده کند

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

گاهی ممکن است نخواهیم تعداد زیادی لینک از قبل prefetch شوند. مثلا در یک جدول بزرگ یا لیست طولانی از آیتم‌ها.

در این حالت می‌توانیم prefetch را غیرفعال کنیم:

<Link href="/blog" prefetch={false}>
  Blog
</Link>

اما این کار trade-off دارد. اگر prefetch خاموش باشد، ممکن است navigation کمی کندتر احساس شود، چون اطلاعات route فقط بعد از کلیک کاربر دریافت می‌شود.

پس بهتر است prefetch={false} را فقط زمانی استفاده کنیم که دلیل مشخصی داریم.


ساخت صفحه لیست بلاگ

برای صفحه بلاگ:

app/blog/page.tsx

مثلا فعلا می‌توانیم یک لیست ساده داشته باشیم:

// app/blog/page.tsx
import Link from 'next/link'

const posts = [
  {
    slug: 'getting-started-with-nextjs',
    title: 'Getting Started with Next.js',
  },
  {
    slug: 'project-structure-in-nextjs',
    title: 'Project Structure in Next.js',
  },
  {
    slug: 'routing-and-layouts-in-nextjs',
    title: 'Routing and Layouts in Next.js',
  },
]

export default function BlogPage() {
  return (
    <main>
      <h1>Blog</h1>

      <ul>
        {posts.map((post) => (
          <li key={post.slug}>
            <Link href={`/blog/${post.slug}`}>
              {post.title}
            </Link>
          </li>
        ))}
      </ul>
    </main>
  )
}

در این مثال، هر پست به یک مسیر داینامیک لینک می‌شود.

مثلا:

/blog/getting-started-with-nextjs
/blog/project-structure-in-nextjs
/blog/routing-and-layouts-in-nextjs

Dynamic Route چیست؟

گاهی مسیرها از قبل ثابت نیستند و بر اساس داده ساخته می‌شوند.

مثلا در بلاگ، نمی‌خواهیم برای هر پست یک پوشه جدا با نام واقعی آن بسازیم. به‌جای آن یک dynamic segment می‌سازیم:

app/blog/[slug]/page.tsx

نام [slug] یعنی این بخش از URL متغیر است.

مثلا همه این مسیرها توسط همین فایل کنترل می‌شوند:

/blog/nextjs-routing
/blog/prisma-with-postgresql
/blog/dashboard-auth

خواندن params در Next.js 16

در Next.js 16، در App Router، params در pageهای داینامیک به‌صورت Promise دریافت می‌شود.

مثلا:

// app/blog/[slug]/page.tsx
export default async function BlogPostPage({
  params,
}: {
  params: Promise<{ slug: string }>
}) {
  const { slug } = await params

  return (
    <main>
      <h1>Blog Post</h1>
      <p>Current slug: {slug}</p>
    </main>
  )
}

نکته مهم این است که در این نسخه، باید params را await کنیم.

این تفاوت برای هماهنگی با APIهای جدید App Router مهم است و اگر با نمونه‌های قدیمی‌تر Next.js کار کرده باشی، ممکن است ببینی که در آن‌ها params مستقیم استفاده شده است. اما در این سری آموزشی، مطابق Next.js 16 از حالت Promise استفاده می‌کنیم.


استفاده از PageProps

Next.js در نسخه‌های جدید helper typeهایی مثل PageProps و LayoutProps در اختیار ما می‌گذارد.

این typeها global هستند و نیاز به import ندارند. بعد از اجرای دستورهایی مثل next dev، next build یا next typegen تولید می‌شوند.

برای صفحه داینامیک بلاگ می‌توانیم بنویسیم:

// app/blog/[slug]/page.tsx
export default async function BlogPostPage(
  props: PageProps<'/blog/[slug]'>
) {
  const { slug } = await props.params

  return (
    <main>
      <h1>Blog Post</h1>
      <p>Current slug: {slug}</p>
    </main>
  )
}

این روش باعث می‌شود type مربوط به route از خود ساختار مسیر استنتاج شود.

برای پروژه ما، استفاده از PageProps در routeهای مهم مثل بلاگ و پروژه‌ها می‌تواند تمیزتر و امن‌تر باشد.


ساخت dynamic route برای پروژه‌ها

برای پروژه‌ها هم ساختار مشابهی داریم:

app/projects/page.tsx
app/projects/[slug]/page.tsx

صفحه لیست پروژه‌ها:

// app/projects/page.tsx
import Link from 'next/link'

const projects = [
  {
    slug: 'portfolio-dashboard',
    title: 'Portfolio Dashboard',
  },
  {
    slug: 'blog-cms',
    title: 'Blog CMS',
  },
]

export default function ProjectsPage() {
  return (
    <main>
      <h1>Projects</h1>

      <ul>
        {projects.map((project) => (
          <li key={project.slug}>
            <Link href={`/projects/${project.slug}`}>
              {project.title}
            </Link>
          </li>
        ))}
      </ul>
    </main>
  )
}

صفحه جزئیات پروژه:

// app/projects/[slug]/page.tsx
export default async function ProjectDetailsPage(
  props: PageProps<'/projects/[slug]'>
) {
  const { slug } = await props.params

  return (
    <main>
      <h1>Project Details</h1>
      <p>Current project: {slug}</p>
    </main>
  )
}

Nested Layout چیست؟

در App Router، layoutها می‌توانند تو در تو باشند.

Root Layout کل پروژه را پوشش می‌دهد:

app/layout.tsx

اما می‌توانیم برای یک بخش خاص، layout جدا بسازیم.

مثلا برای بلاگ:

app/blog/layout.tsx

نمونه:

// app/blog/layout.tsx
export default function BlogLayout({
  children,
}: {
  children: React.ReactNode
}) {
  return (
    <section>
      <header>
        <h2>Blog</h2>
        <p>Articles, tutorials, and project notes.</p>
      </header>

      {children}
    </section>
  )
}

حالا این layout هم صفحه /blog را پوشش می‌دهد، هم صفحه‌های داینامیک مثل:

/blog/nextjs-routing

ترتیب wrap شدن layoutها این‌طور است:

app/layout.tsx
  app/blog/layout.tsx
    app/blog/page.tsx

یا برای پست داینامیک:

app/layout.tsx
  app/blog/layout.tsx
    app/blog/[slug]/page.tsx

Layout اختصاصی برای داشبورد

داشبورد معمولا UI متفاوتی دارد. مثلا sidebar، header مدیریتی و لینک‌های داخلی خودش را دارد.

برای همین بهتر است برای آن layout جدا بسازیم:

app/dashboard/layout.tsx

نمونه:

// app/dashboard/layout.tsx
import Link from 'next/link'

export default function DashboardLayout({
  children,
}: {
  children: React.ReactNode
}) {
  return (
    <section>
      <aside>
        <nav>
          <Link href="/dashboard">Overview</Link>
          <Link href="/dashboard/posts">Posts</Link>
          <Link href="/dashboard/projects">Projects</Link>
        </nav>
      </aside>

      <main>
        {children}
      </main>
    </section>
  )
}

این layout برای تمام routeهای زیر اعمال می‌شود:

/dashboard
/dashboard/posts
/dashboard/projects

در آینده، وقتی authentication را اضافه کنیم، مسیرهای داشبورد را با کمک proxy.ts و بررسی session محافظت می‌کنیم.


searchParams چیست؟

گاهی اطلاعات صفحه از query string گرفته می‌شود.

مثلا:

/blog?page=2
/projects?category=nextjs
/dashboard/posts?status=draft

در Server Component page می‌توانیم searchParams را از props بخوانیم.

در Next.js 16، searchParams هم Promise است:

// app/blog/page.tsx
export default async function BlogPage({
  searchParams,
}: {
  searchParams: Promise<{
    page?: string
    category?: string
  }>
}) {
  const { page = '1', category } = await searchParams

  return (
    <main>
      <h1>Blog</h1>
      <p>Current page: {page}</p>
      <p>Category: {category ?? 'all'}</p>
    </main>
  )
}

این قابلیت برای مواردی مثل این‌ها کاربرد دارد:

  • صفحه‌بندی بلاگ
  • فیلتر پروژه‌ها
  • جست‌وجوی پست‌ها
  • فیلتر آیتم‌های داشبورد

نکته مهم: استفاده از searchParams در یک page باعث می‌شود آن page به incoming request وابسته شود و وارد dynamic rendering شود.


چه زمانی از searchParams و چه زمانی از useSearchParams استفاده کنیم؟

در Next.js دو روش رایج برای خواندن query string داریم.

اگر query برای دریافت داده سمت سرور لازم است، از searchParams در page استفاده می‌کنیم.

مثلا:

/blog?page=2

اگر قرار است بر اساس page از دیتابیس پست‌ها را بگیریم، searchParams مناسب است.

اما اگر فقط یک فیلتر ساده سمت کلاینت داریم، مثلا لیستی قبلا load شده و فقط می‌خواهیم در مرورگر فیلترش کنیم، می‌توانیم از useSearchParams استفاده کنیم.

useSearchParams مخصوص Client Component است و باید در فایلی استفاده شود که بالای آن use client قرار دارد.


نمونه Client Component با useSearchParams

'use client'

import { useSearchParams } from 'next/navigation'

export function BlogFilters() {
  const searchParams = useSearchParams()
  const category = searchParams.get('category') ?? 'all'

  return (
    <div>
      <p>Selected category: {category}</p>
    </div>
  )
}

این روش برای UIهای تعاملی سمت کلاینت مناسب است.


استفاده از Native History API

Next.js اجازه می‌دهد از window.history.pushState و window.history.replaceState استفاده کنیم و این تغییرات با Router هماهنگ می‌شوند.

مثلا برای تغییر query string بدون reload صفحه:

'use client'

import { useSearchParams } from 'next/navigation'

export function SortProjects() {
  const searchParams = useSearchParams()

  function updateSorting(sortOrder: string) {
    const params = new URLSearchParams(searchParams.toString())
    params.set('sort', sortOrder)

    window.history.pushState(null, '', `?${params.toString()}`)
  }

  return (
    <div>
      <button onClick={() => updateSorting('asc')}>
        Sort Ascending
      </button>
      <button onClick={() => updateSorting('desc')}>
        Sort Descending
      </button>
    </div>
  )
}

pushState یک entry جدید به history اضافه می‌کند، یعنی کاربر می‌تواند با دکمه back به حالت قبلی برگردد.

اما replaceState مقدار فعلی history را جایگزین می‌کند و entry جدید نمی‌سازد.


ساخت loading.tsx

در مسیرهای dynamic، گاهی صفحه برای دریافت داده از دیتابیس یا API کمی زمان می‌برد. اگر هیچ loading UI نداشته باشیم، کاربر ممکن است حس کند سایت کند یا متوقف شده است.

در Next.js می‌توانیم داخل هر route فایل loading.tsx بسازیم.

مثلا برای صفحه جزئیات بلاگ:

app/blog/[slug]/loading.tsx

نمونه:

// app/blog/[slug]/loading.tsx
export default function Loading() {
  return (
    <main>
      <p>Loading blog post...</p>
    </main>
  )
}

Next.js به‌صورت خودکار محتوای page.tsx همان route را داخل یک Suspense boundary قرار می‌دهد و تا زمانی که محتوای اصلی آماده شود، loading.tsx را نمایش می‌دهد.


چرا loading.tsx مهم است؟

loading.tsx چند مزیت مهم دارد:

  • کاربر سریع‌تر feedback می‌گیرد
  • navigation به routeهای dynamic روان‌تر می‌شود
  • layoutهای مشترک interactive باقی می‌مانند
  • کاربر حس نمی‌کند سایت قفل شده است
  • در routeهای dynamic، partial prefetch بهتر انجام می‌شود

برای پروژه ما، بهتر است این مسیرها loading UI داشته باشند:

app/blog/[slug]/loading.tsx
app/projects/[slug]/loading.tsx
app/dashboard/loading.tsx

در آینده می‌توانیم به‌جای متن ساده، skeletonهای واقعی با Tailwind CSS بسازیم.


generateStaticParams برای routeهای داینامیک

اگر dynamic routeهایی داریم که مقادیرشان از قبل مشخص است، می‌توانیم از generateStaticParams استفاده کنیم تا Next.js آن‌ها را در build time آماده کند.

مثلا برای پست‌های بلاگ:

// app/blog/[slug]/page.tsx
const posts = [
  {
    slug: 'getting-started-with-nextjs',
  },
  {
    slug: 'project-structure-in-nextjs',
  },
  {
    slug: 'routing-and-layouts-in-nextjs',
  },
]

export async function generateStaticParams() {
  return posts.map((post) => ({
    slug: post.slug,
  }))
}

export default async function BlogPostPage(
  props: PageProps<'/blog/[slug]'>
) {
  const { slug } = await props.params

  return (
    <main>
      <h1>Blog Post</h1>
      <p>Current slug: {slug}</p>
    </main>
  )
}

این کار برای بلاگ خیلی مهم است، چون بسیاری از پست‌ها می‌توانند static باشند و لازم نیست در هر request از نو ساخته شوند.

بعدا وقتی Prisma و دیتابیس را اضافه کنیم، generateStaticParams می‌تواند slug پست‌های منتشرشده را از دیتابیس بخواند.


مسیرهای داشبورد

برای داشبورد، بهتر است از همان ابتدا ساختار routeها را مشخص کنیم:

app/dashboard/
├── layout.tsx
├── page.tsx
├── posts/
│   ├── page.tsx
│   └── new/
│       └── page.tsx
└── projects/
    ├── page.tsx
    └── new/
        └── page.tsx

این ساختار مسیرهای زیر را می‌سازد:

/dashboard
/dashboard/posts
/dashboard/posts/new
/dashboard/projects
/dashboard/projects/new

صفحه اصلی داشبورد:

// app/dashboard/page.tsx
export default function DashboardPage() {
  return (
    <main>
      <h1>Dashboard</h1>
      <p>Manage posts, projects, and portfolio content.</p>
    </main>
  )
}

صفحه مدیریت پست‌ها:

// app/dashboard/posts/page.tsx
export default function DashboardPostsPage() {
  return (
    <main>
      <h1>Posts</h1>
      <p>Manage blog posts.</p>
    </main>
  )
}

صفحه ساخت پست جدید:

// app/dashboard/posts/new/page.tsx
export default function NewPostPage() {
  return (
    <main>
      <h1>New Post</h1>
      <p>Create a new blog post.</p>
    </main>
  )
}

Routeهای عمومی و خصوصی

در پروژه ما routeها را می‌توانیم به دو گروه کلی تقسیم کنیم.

Routeهای عمومی:

/
 /about
/contact
/blog
/blog/[slug]
/projects
/projects/[slug]
/login

و routeهای خصوصی:

/dashboard
/dashboard/posts
/dashboard/posts/new
/dashboard/projects
/dashboard/projects/new

مسیرهای عمومی برای همه کاربران قابل مشاهده هستند. اما مسیرهای داشبورد باید فقط برای ادمین قابل دسترسی باشند.

در Next.js 16، برای محافظت اولیه از این مسیرها، در آینده از proxy.ts استفاده می‌کنیم؛ نه middleware.ts.

یعنی اگر بخواهیم requestهای ورودی به /dashboard را قبل از رسیدن به page بررسی کنیم، فایل ما چنین نامی خواهد داشت:

proxy.ts

نه:

middleware.ts

این نکته با تغییرات Next.js 16 هماهنگ است.


ساختار پیشنهادی بعد از این پست

تا اینجا، ساختار پروژه ما می‌تواند شبیه این باشد:

app/
├── layout.tsx
├── page.tsx
├── loading.tsx
├── about/
│   └── page.tsx
├── contact/
│   └── page.tsx
├── blog/
│   ├── layout.tsx
│   ├── page.tsx
│   └── [slug]/
│       ├── page.tsx
│       └── loading.tsx
├── projects/
│   ├── page.tsx
│   └── [slug]/
│       ├── page.tsx
│       └── loading.tsx
├── login/
│   └── page.tsx
└── dashboard/
    ├── layout.tsx
    ├── loading.tsx
    ├── page.tsx
    ├── posts/
    │   ├── page.tsx
    │   └── new/
    │       └── page.tsx
    └── projects/
        ├── page.tsx
        └── new/
            └── page.tsx

این ساختار هنوز ساده است، اما برای ادامه پروژه کاملا مناسب است.


چند نکته مهم برای Next.js 16

در این سری آموزشی، برای هماهنگی با Next.js 16 این نکات را رعایت می‌کنیم:

  • در routeهای داینامیک، params را به‌صورت Promise در نظر می‌گیریم
  • در pageهایی که query string لازم دارند، searchParams را به‌صورت Promise استفاده می‌کنیم
  • برای navigation داخلی، از Link استفاده می‌کنیم
  • برای routeهای dynamic و کند، loading.tsx می‌سازیم
  • برای محافظت request-level از routeها، در آینده از proxy.ts استفاده می‌کنیم
  • برای type safety بهتر، می‌توانیم از PageProps و LayoutProps استفاده کنیم
  • Root Layout باید شامل html و body باشد

تمرین پیشنهادی

برای تثبیت این بخش، بهتر است این routeها را در پروژه بسازی:

app/about/page.tsx
app/contact/page.tsx
app/blog/page.tsx
app/blog/[slug]/page.tsx
app/projects/page.tsx
app/projects/[slug]/page.tsx
app/dashboard/page.tsx
app/dashboard/posts/page.tsx
app/dashboard/projects/page.tsx

بعد داخل app/layout.tsx یک navigation ساده با Link بساز:

<Link href="/">Home</Link>
<Link href="/about">About</Link>
<Link href="/projects">Projects</Link>
<Link href="/blog">Blog</Link>
<Link href="/contact">Contact</Link>

برای routeهای داینامیک هم یک loading.tsx ساده قرار بده تا navigation تجربه بهتری داشته باشد.


جمع‌بندی

در این پست یاد گرفتیم که App Router در Next.js 16 چطور مسیرها را از روی ساختار فایل‌ها می‌سازد.

مهم‌ترین نکات این بخش:

  • page.tsx برای ساخت صفحه استفاده می‌شود
  • layout.tsx برای UI مشترک بین صفحه‌ها استفاده می‌شود
  • Root Layout الزامی است و باید html و body داشته باشد
  • پوشه‌ها route segment می‌سازند
  • [slug] برای ساخت dynamic route استفاده می‌شود
  • در Next.js 16، params و searchParams در pageها Promise هستند
  • Link روش اصلی navigation داخلی در Next.js است
  • loading.tsx تجربه navigation را بهتر می‌کند
  • generateStaticParams برای prerender کردن routeهای داینامیک کاربرد دارد
  • برای مسیرهای محافظت‌شده، در آینده از proxy.ts استفاده می‌کنیم
// 0 comments
#Next.js#Linking#Navigation#مسیریابی

نظرات (0)

نظر خود را بنویسید