# Next.js App Router Architecture Modern file-system based routing with React Server Components support. ## File Conventions Special files define route behavior: - `page.tsx` - Page UI, makes route publicly accessible - `layout.tsx` - Shared UI wrapper for segment and children - `loading.tsx` - Loading UI, automatically wraps page in Suspense - `error.tsx` - Error UI, wraps page in Error Boundary - `not-found.tsx` - 404 UI for route segment - `route.ts` - API endpoint (Route Handler) - `template.tsx` - Re-rendered layout (doesn't preserve state) - `default.tsx` - Fallback for parallel routes ## Basic Routing ### Static Routes ``` app/ ├── page.tsx → / ├── about/ │ └── page.tsx → /about ├── blog/ │ └── page.tsx → /blog └── contact/ └── page.tsx → /contact ``` ### Dynamic Routes Single parameter: ```tsx // app/blog/[slug]/page.tsx export default function BlogPost({ params }: { params: { slug: string } }) { return

Post: {params.slug}

} // Matches: /blog/hello-world, /blog/my-post ``` Catch-all segments: ```tsx // app/shop/[...slug]/page.tsx export default function Shop({ params }: { params: { slug: string[] } }) { return

Category: {params.slug.join('/')}

} // Matches: /shop/clothes, /shop/clothes/shirts, /shop/clothes/shirts/red ``` Optional catch-all: ```tsx // app/docs/[[...slug]]/page.tsx // Matches: /docs, /docs/getting-started, /docs/api/reference ``` ## Layouts ### Root Layout (Required) Must include `` and `` tags: ```tsx // app/layout.tsx export default function RootLayout({ children, }: { children: React.ReactNode }) { return (
Global Header
{children} ) } ``` ### Nested Layouts ```tsx // app/dashboard/layout.tsx export default function DashboardLayout({ children, }: { children: React.ReactNode }) { return (
{children}
) } ``` Layout characteristics: - Preserve state during navigation - Do not re-render on navigation between child routes - Can fetch data - Cannot access pathname or searchParams (use Client Component) ## Route Groups Organize routes without affecting URL structure: ``` app/ ├── (marketing)/ # Group without URL segment │ ├── about/page.tsx → /about │ ├── blog/page.tsx → /blog │ └── layout.tsx # Marketing layout ├── (shop)/ │ ├── products/page.tsx → /products │ ├── cart/page.tsx → /cart │ └── layout.tsx # Shop layout └── layout.tsx # Root layout ``` Use cases: - Multiple root layouts - Organize code by feature/team - Different layouts for different sections ## Parallel Routes Render multiple pages simultaneously in same layout: ``` app/ ├── @team/ # Named slot │ └── page.tsx ├── @analytics/ # Named slot │ └── page.tsx ├── page.tsx # Default children └── layout.tsx # Consumes slots ``` ```tsx // app/layout.tsx export default function Layout({ children, team, analytics, }: { children: React.ReactNode team: React.ReactNode analytics: React.ReactNode }) { return ( <> {children}
{team} {analytics}
) } ``` Use cases: - Split views (dashboards) - Modals - Conditional rendering based on auth state ## Intercepting Routes Intercept navigation to show content in different context: ``` app/ ├── feed/ │ └── page.tsx ├── photo/ │ └── [id]/ │ └── page.tsx # Full photo page └── (..)photo/ # Intercepts /photo/[id] └── [id]/ └── page.tsx # Modal photo view ``` Matching conventions: - `(.)` - Match same level - `(..)` - Match one level above - `(..)(..)` - Match two levels above - `(...)` - Match from app root Use case: Display modal when navigating from feed, show full page when URL accessed directly ## Loading States ### Loading File Automatically wraps page in Suspense: ```tsx // app/dashboard/loading.tsx export default function Loading() { return
Loading dashboard...
} ``` ### Manual Suspense Fine-grained control: ```tsx // app/page.tsx import { Suspense } from 'react' async function Posts() { const posts = await fetchPosts() return } export default function Page() { return (

My Blog

Loading posts...
}> ) } ``` ## Error Handling ### Error File Wraps segment in Error Boundary: ```tsx // app/error.tsx 'use client' // Error components must be Client Components export default function Error({ error, reset, }: { error: Error & { digest?: string } reset: () => void }) { return (

Something went wrong!

{error.message}

) } ``` ### Global Error Catches errors in root layout: ```tsx // app/global-error.tsx 'use client' export default function GlobalError({ error, reset, }: { error: Error & { digest?: string } reset: () => void }) { return (

Application Error!

) } ``` ### Not Found ```tsx // app/blog/[slug]/page.tsx import { notFound } from 'next/navigation' export default async function Post({ params }: { params: { slug: string } }) { const post = await getPost(params.slug) if (!post) { notFound() // Triggers not-found.tsx } return
{post.content}
} // app/blog/[slug]/not-found.tsx export default function NotFound() { return

Post not found

} ``` ## Navigation ### Link Component ```tsx import Link from 'next/link' // Basic link About // Dynamic route Read Post // With object Read Post // Prefetch control Dashboard // Replace history Search ``` ### useRouter Hook (Client) ```tsx 'use client' import { useRouter } from 'next/navigation' export function NavigateButton() { const router = useRouter() return ( <> ) } ``` ### Programmatic Navigation (Server) ```tsx import { redirect } from 'next/navigation' export default async function Page() { const session = await getSession() if (!session) { redirect('/login') } return
Protected content
} ``` ## Accessing Route Information ### searchParams (Server) ```tsx // app/shop/page.tsx export default function Shop({ searchParams, }: { searchParams: { sort?: string; filter?: string } }) { const sort = searchParams.sort || 'newest' const filter = searchParams.filter return
Showing: {filter}, sorted by {sort}
} // Accessed via: /shop?sort=price&filter=shirts ``` ### useSearchParams (Client) ```tsx 'use client' import { useSearchParams } from 'next/navigation' export function SearchFilter() { const searchParams = useSearchParams() const query = searchParams.get('q') return
Search query: {query}
} ``` ### usePathname (Client) ```tsx 'use client' import { usePathname } from 'next/navigation' import Link from 'next/link' export function Navigation() { const pathname = usePathname() return ( ) } ``` ## Project Structure Best Practices ``` app/ ├── (auth)/ # Route group for auth pages │ ├── login/ │ ├── signup/ │ └── layout.tsx # Auth layout ├── (dashboard)/ # Route group for dashboard │ ├── dashboard/ │ ├── settings/ │ └── layout.tsx # Dashboard layout ├── api/ # API routes │ ├── auth/ │ └── posts/ ├── _components/ # Private folder (not routes) │ ├── header.tsx │ └── footer.tsx ├── _lib/ # Private utilities │ ├── auth.ts │ └── db.ts ├── layout.tsx # Root layout ├── page.tsx # Home page ├── loading.tsx ├── error.tsx └── not-found.tsx ``` Use underscore prefix for folders that shouldn't be routes.