442 lines
		
	
	
		
			9.0 KiB
		
	
	
	
		
			Markdown
		
	
	
	
	
	
			
		
		
	
	
			442 lines
		
	
	
		
			9.0 KiB
		
	
	
	
		
			Markdown
		
	
	
	
	
	
| # Next.js App Router File Templates
 | |
| 
 | |
| This document provides basic templates for the common file types used in a Next.js application with the App Router. These templates follow the current best practices for Next.js 15.
 | |
| 
 | |
| ## Page Templates
 | |
| 
 | |
| ### Basic Page (`page.tsx`)
 | |
| 
 | |
| ```tsx
 | |
| import { Metadata } from 'next'
 | |
| 
 | |
| export const metadata: Metadata = {
 | |
|   title: 'Page Title',
 | |
|   description: 'Page description',
 | |
| }
 | |
| 
 | |
| export default function Page() {
 | |
|   return (
 | |
|     <div>
 | |
|       <h1>Page Content</h1>
 | |
|       {/* Page content goes here */}
 | |
|     </div>
 | |
|   )
 | |
| }
 | |
| ```
 | |
| 
 | |
| ### Dynamic Route Page (`[slug]/page.tsx`)
 | |
| 
 | |
| ```tsx
 | |
| export async function generateMetadata({ params }) {
 | |
|   return {
 | |
|     title: `Page for ${params.slug}`,
 | |
|     description: `Description for ${params.slug}`,
 | |
|   }
 | |
| }
 | |
| 
 | |
| export default function DynamicPage({ params }) {
 | |
|   const { slug } = params
 | |
|   
 | |
|   return (
 | |
|     <div>
 | |
|       <h1>Dynamic Page: {slug}</h1>
 | |
|       {/* Dynamic page content goes here */}
 | |
|     </div>
 | |
|   )
 | |
| }
 | |
| ```
 | |
| 
 | |
| ## Layout Templates
 | |
| 
 | |
| ### Root Layout (`layout.tsx`)
 | |
| 
 | |
| ```tsx
 | |
| import { Metadata } from 'next'
 | |
| import './globals.css'
 | |
| 
 | |
| export const metadata: Metadata = {
 | |
|   title: {
 | |
|     template: '%s | Site Name',
 | |
|     default: 'Site Name',
 | |
|   },
 | |
|   description: 'Site description',
 | |
| }
 | |
| 
 | |
| export default function RootLayout({ children }) {
 | |
|   return (
 | |
|     <html lang="en">
 | |
|       <body>
 | |
|         <header>
 | |
|           {/* Header content */}
 | |
|         </header>
 | |
|         <main>{children}</main>
 | |
|         <footer>
 | |
|           {/* Footer content */}
 | |
|         </footer>
 | |
|       </body>
 | |
|     </html>
 | |
|   )
 | |
| }
 | |
| ```
 | |
| 
 | |
| ### Nested Layout (`(dashboard)/layout.tsx`)
 | |
| 
 | |
| ```tsx
 | |
| export default function DashboardLayout({ children }) {
 | |
|   return (
 | |
|     <div className="dashboard-layout">
 | |
|       <nav className="dashboard-nav">
 | |
|         {/* Dashboard navigation */}
 | |
|       </nav>
 | |
|       <div className="dashboard-content">
 | |
|         {children}
 | |
|       </div>
 | |
|     </div>
 | |
|   )
 | |
| }
 | |
| ```
 | |
| 
 | |
| ### Auth Layout with Session Check (`(authenticated)/layout.tsx`)
 | |
| 
 | |
| ```tsx
 | |
| import { redirect } from 'next/navigation'
 | |
| 
 | |
| // This is a Server Component
 | |
| export default async function AuthLayout({ children }) {
 | |
|   const session = await getSession() // Replace with your auth solution
 | |
|   
 | |
|   if (!session) {
 | |
|     redirect('/login')
 | |
|   }
 | |
| 
 | |
|   return <>{children}</>
 | |
| }
 | |
| ```
 | |
| 
 | |
| ## Error Handling Templates
 | |
| 
 | |
| ### Not Found (`not-found.tsx`)
 | |
| 
 | |
| ```tsx
 | |
| import Link from 'next/link'
 | |
| 
 | |
| export const metadata = {
 | |
|   title: 'Not Found',
 | |
|   description: 'Page not found',
 | |
| }
 | |
| 
 | |
| export default function NotFound() {
 | |
|   return (
 | |
|     <div className="error-container">
 | |
|       <h1>404 - Page Not Found</h1>
 | |
|       <p>The page you are looking for does not exist.</p>
 | |
|       <Link href="/">
 | |
|         Return to Home
 | |
|       </Link>
 | |
|     </div>
 | |
|   )
 | |
| }
 | |
| ```
 | |
| 
 | |
| ### Error Page (`error.tsx`)
 | |
| 
 | |
| ```tsx
 | |
| 'use client' // Error components must be Client Components
 | |
| 
 | |
| import { useEffect } from 'react'
 | |
| 
 | |
| export default function Error({ error, reset }) {
 | |
|   useEffect(() => {
 | |
|     // Log the error to an error reporting service
 | |
|     console.error(error)
 | |
|   }, [error])
 | |
| 
 | |
|   return (
 | |
|     <div className="error-container">
 | |
|       <h2>Something went wrong!</h2>
 | |
|       <button
 | |
|         onClick={
 | |
|           // Attempt to recover by trying to re-render the segment
 | |
|           () => reset()
 | |
|         }
 | |
|       >
 | |
|         Try again
 | |
|       </button>
 | |
|     </div>
 | |
|   )
 | |
| }
 | |
| ```
 | |
| 
 | |
| ### Global Error (`global-error.tsx`)
 | |
| 
 | |
| ```tsx
 | |
| 'use client'
 | |
| 
 | |
| export default function GlobalError({ error, reset }) {
 | |
|   return (
 | |
|     <html>
 | |
|       <body>
 | |
|         <div className="global-error">
 | |
|           <h2>Something went wrong!</h2>
 | |
|           <button onClick={() => reset()}>Try again</button>
 | |
|         </div>
 | |
|       </body>
 | |
|     </html>
 | |
|   )
 | |
| }
 | |
| ```
 | |
| 
 | |
| ## Loading States
 | |
| 
 | |
| ### Loading Component (`loading.tsx`)
 | |
| 
 | |
| ```tsx
 | |
| export default function Loading() {
 | |
|   return (
 | |
|     <div className="loading-container">
 | |
|       <div className="loading-spinner"></div>
 | |
|       <p>Loading...</p>
 | |
|     </div>
 | |
|   )
 | |
| }
 | |
| ```
 | |
| 
 | |
| ## Route Handlers
 | |
| 
 | |
| ### API Route (`api/route.ts`)
 | |
| 
 | |
| ```ts
 | |
| import { NextResponse } from 'next/server'
 | |
|  
 | |
| export async function GET(request) {
 | |
|   // Handle GET request
 | |
|   return NextResponse.json({ message: 'Hello World' })
 | |
| }
 | |
| 
 | |
| export async function POST(request) {
 | |
|   // Handle POST request
 | |
|   const body = await request.json()
 | |
|   return NextResponse.json({ received: body })
 | |
| }
 | |
| ```
 | |
| 
 | |
| ### Dynamic API Route (`api/[slug]/route.ts`)
 | |
| 
 | |
| ```ts
 | |
| import { NextResponse } from 'next/server'
 | |
| 
 | |
| export async function GET(request, { params }) {
 | |
|   const { slug } = params
 | |
|   return NextResponse.json({ slug })
 | |
| }
 | |
| ```
 | |
| 
 | |
| ## Middleware
 | |
| 
 | |
| ### Middleware (`middleware.ts`)
 | |
| 
 | |
| Place this file in the root of your project:
 | |
| 
 | |
| ```ts
 | |
| import { NextResponse } from 'next/server'
 | |
| import type { NextRequest } from 'next/server'
 | |
|  
 | |
| // This function runs before requests are completed
 | |
| export function middleware(request: NextRequest) {
 | |
|   const currentUrl = request.nextUrl.clone()
 | |
|   
 | |
|   // Example: check if user is authenticated
 | |
|   const isAuthenticated = checkAuth(request)
 | |
|   
 | |
|   if (!isAuthenticated && currentUrl.pathname.startsWith('/protected')) {
 | |
|     return NextResponse.redirect(new URL('/login', request.url))
 | |
|   }
 | |
|   
 | |
|   return NextResponse.next()
 | |
| }
 | |
| 
 | |
| // Configure matcher for paths that should trigger middleware
 | |
| export const config = {
 | |
|   matcher: ['/protected/:path*', '/api/:path*'],
 | |
| }
 | |
| 
 | |
| // Example auth check function
 | |
| function checkAuth(request) {
 | |
|   // Implement your auth check logic here
 | |
|   return !!request.cookies.get('auth-token')
 | |
| }
 | |
| ```
 | |
| 
 | |
| ## Utilities - Server Actions 
 | |
| 
 | |
| ### Form Actions (`actions.ts`)
 | |
| 
 | |
| ```ts
 | |
| 'use server'
 | |
| 
 | |
| export async function submitForm(formData: FormData) {
 | |
|   // Validate data
 | |
|   const name = formData.get('name')
 | |
|   const email = formData.get('email')
 | |
|   
 | |
|   // Process data
 | |
|   try {
 | |
|     // Save to database or send to API
 | |
|     await saveToDatabase({ name, email })
 | |
|     return { success: true }
 | |
|   } catch (error) {
 | |
|     return { success: false, error: error.message }
 | |
|   }
 | |
| }
 | |
| 
 | |
| async function saveToDatabase(data) {
 | |
|   // Implementation for database interaction
 | |
| }
 | |
| ```
 | |
| 
 | |
| ## Server Component with Data Fetching
 | |
| 
 | |
| ### Data Fetching in Server Component (`dashboard/page.tsx`)
 | |
| 
 | |
| ```tsx
 | |
| // This is a Server Component
 | |
| export default async function DashboardPage() {
 | |
|   // This data fetching happens on the server
 | |
|   const data = await fetchDashboardData()
 | |
|   
 | |
|   return (
 | |
|     <div>
 | |
|       <h1>Dashboard</h1>
 | |
|       <div className="dashboard-stats">
 | |
|         {data.map(item => (
 | |
|           <div key={item.id} className="stat-card">
 | |
|             <h3>{item.title}</h3>
 | |
|             <p>{item.value}</p>
 | |
|           </div>
 | |
|         ))}
 | |
|       </div>
 | |
|     </div>
 | |
|   )
 | |
| }
 | |
| 
 | |
| async function fetchDashboardData() {
 | |
|   // API or database call
 | |
|   const res = await fetch('https://api.example.com/dashboard-data', {
 | |
|     cache: 'no-store' // Don't cache this data
 | |
|   })
 | |
|   
 | |
|   if (!res.ok) {
 | |
|     throw new Error('Failed to fetch dashboard data')
 | |
|   }
 | |
|   
 | |
|   return res.json()
 | |
| }
 | |
| ```
 | |
| 
 | |
| ## Client Component with Hooks
 | |
| 
 | |
| ### Interactive Client Component (`components/Counter.tsx`)
 | |
| 
 | |
| ```tsx
 | |
| 'use client' // Mark as Client Component
 | |
| 
 | |
| import { useState } from 'react'
 | |
| 
 | |
| export default function Counter() {
 | |
|   const [count, setCount] = useState(0)
 | |
|   
 | |
|   return (
 | |
|     <div>
 | |
|       <p>Count: {count}</p>
 | |
|       <button onClick={() => setCount(count + 1)}>
 | |
|         Increment
 | |
|       </button>
 | |
|     </div>
 | |
|   )
 | |
| }
 | |
| ```
 | |
| 
 | |
| ## Parallel Routes
 | |
| 
 | |
| ### Parallel Route Layout (`@dashboard/page.tsx` and `@profile/page.tsx`)
 | |
| 
 | |
| In your directory structure:
 | |
| ```
 | |
| app/
 | |
|   layout.tsx
 | |
|   [user]/
 | |
|     layout.tsx
 | |
|     page.tsx
 | |
|     @dashboard/
 | |
|       page.tsx
 | |
|     @profile/
 | |
|       page.tsx
 | |
| ```
 | |
| 
 | |
| In `[user]/layout.tsx`:
 | |
| ```tsx
 | |
| export default function UserLayout({ children, dashboard, profile }) {
 | |
|   return (
 | |
|     <div className="user-page">
 | |
|       <div className="sidebar">
 | |
|         {/* Sidebar navigation */}
 | |
|       </div>
 | |
|       <div className="main-content">
 | |
|         {children}
 | |
|       </div>
 | |
|       <div className="dashboard-slot">
 | |
|         {dashboard}
 | |
|       </div>
 | |
|       <div className="profile-slot">
 | |
|         {profile}
 | |
|       </div>
 | |
|     </div>
 | |
|   )
 | |
| }
 | |
| ```
 | |
| 
 | |
| ## Route Interception
 | |
| 
 | |
| ### Photo Modal Example (`./(.)photos/[id]/page.tsx`)
 | |
| 
 | |
| The directory structure might look like:
 | |
| ```
 | |
| app/
 | |
|   photos/
 | |
|     page.tsx
 | |
|     [id]/
 | |
|       page.tsx
 | |
|   (.)photos/
 | |
|     [id]/
 | |
|       page.tsx  // This intercepts /photos/[id]
 | |
| ```
 | |
| 
 | |
| In `./(.)photos/[id]/page.tsx`:
 | |
| ```tsx
 | |
| export default function PhotoModal({ params }) {
 | |
|   const { id } = params
 | |
|   
 | |
|   // Fetch photo data based on id
 | |
|   
 | |
|   return (
 | |
|     <div className="modal">
 | |
|       <div className="modal-content">
 | |
|         <h2>Photo {id}</h2>
 | |
|         <img src={`/api/photos/${id}`} alt={`Photo ${id}`} />
 | |
|       </div>
 | |
|     </div>
 | |
|   )
 | |
| }
 | |
| ```
 | |
| 
 | |
| ## References
 | |
| 
 | |
| - [Official Next.js Documentation](https://nextjs.org/docs/app)
 | |
| - [Pages and Layouts](https://nextjs.org/docs/app/building-your-application/routing/pages-and-layouts)
 | |
| - [Route Handlers](https://nextjs.org/docs/app/building-your-application/routing/route-handlers)
 | |
| - [Error Handling](https://nextjs.org/docs/app/building-your-application/routing/error-handling)
 | |
| - [Loading UI and Streaming](https://nextjs.org/docs/app/building-your-application/routing/loading-ui-and-streaming)
 | |
| - [Metadata API](https://nextjs.org/docs/app/building-your-application/optimizing/metadata)
 | |
| - [Server Actions](https://nextjs.org/docs/app/building-your-application/data-fetching/server-actions)
 | |
| - [Middleware](https://nextjs.org/docs/app/building-your-application/routing/middleware)
 |