File Conventions และ Special Files
ไอเดียหลักในหนึ่งประโยค
หัวข้อที่มีชื่อว่า “ไอเดียหลักในหนึ่งประโยค”ใน App Router โฟลเดอร์นิยาม route segment และชื่อไฟล์ที่จองไว้ชุดเล็ก ๆ ให้พฤติกรรมกับแต่ละโฟลเดอร์ — โฟลเดอร์จะเข้าถึงเป็น URL ได้ก็ต่อเมื่อมี page อยู่ข้างใน
โฟลเดอร์คือ segment ส่วนไฟล์พิเศษคือพฤติกรรม
หัวข้อที่มีชื่อว่า “โฟลเดอร์คือ segment ส่วนไฟล์พิเศษคือพฤติกรรม”โฟลเดอร์ใต้ app/ คือ URL segment แต่โฟลเดอร์เปล่า ๆ ไม่ทำอะไร สิ่งที่ทำถูกตัดสินโดยไฟล์ที่จองไว้ข้างใน ชื่อไฟล์แต่ละอันมีหน้าที่เดียว —
page.tsx— UI ของ route และเป็นตัวที่ทำให้ segment นั้น route ได้แบบ publiclayout.tsx— wrapper ที่ใช้ร่วมกัน คงอยู่ข้ามการ navigate และ nest ซ้อนใน parent layoutloading.tsx— Suspense fallback ที่แสดงระหว่าง segment กำลัง stream เข้ามาerror.tsx— error boundary ของ segment และต้องเป็น Client Componentnot-found.tsx— UI ที่ render เมื่อnotFound()ถูกเรียกroute.ts— Route Handler คือ API endpoint แทนที่จะเป็น pagetemplate.tsx— คล้าย layout แต่ re-mount instance ใหม่ทุกครั้งที่ navigate
app/ layout.tsx # root layout — wraps everything page.tsx # "/" dashboard/ layout.tsx # wraps everything under /dashboard loading.tsx # fallback while /dashboard loads error.tsx # catches errors in /dashboard page.tsx # "/dashboard" api/ users/ route.ts # "/api/users" — a Route Handler, not a pagepage กับ layout คือสองอันที่คุณใช้บ่อยสุด
หัวข้อที่มีชื่อว่า “page กับ layout คือสองอันที่คุณใช้บ่อยสุด”page รับ route param และ search param แล้ว return UI ของ segment —
export default async function BlogPost({ params,}: { params: Promise<{ slug: string }>;}) { const { slug } = await params; return <article>Reading: {slug}</article>;}layout ครอบ segment และทุกอย่างที่อยู่ข้างใต้ และเก็บ state ไว้ข้ามการ navigate ระหว่าง sibling page ดังนั้น chrome ที่ใช้ร่วมกันอย่าง sidebar จึงไม่ remount —
export default function DashboardLayout({ children,}: { children: React.ReactNode;}) { return ( <section> <nav>Dashboard nav</nav> {children} </section> );}root layout เป็นสิ่งที่ต้องมี
หัวข้อที่มีชื่อว่า “root layout เป็นสิ่งที่ต้องมี”ทุก App Router app ต้องมี root layout หนึ่งอันที่ app/layout.tsx เป็น layout เดียวที่ render tag <html> และ <body> เพราะ Next.js ไม่ได้ปล่อย tag พวกนี้ให้คุณ —
export default function RootLayout({ children,}: { children: React.ReactNode;}) { return ( <html lang="en"> <body>{children}</body> </html> );}มีสองกฎที่ตามมาจากทั้งหมดนี้ กฎแรก โฟลเดอร์ที่ไม่มี page.tsx (หรือ route.ts) จะ route ไม่ได้ — คุณ nest โฟลเดอร์เพื่อจัดระเบียบไฟล์ล้วน ๆ ได้ และจะไม่ตอบสนองต่อ URL เลย กฎที่สอง error.tsx ต้องขึ้นต้นด้วย "use client" เพราะ error boundary พึ่งพา React ฝั่ง client
flowchart LR Folder[dashboard folder] --> Layout[layout wraps and persists] Layout --> Loading[loading is the Suspense fallback] Layout --> Error[error catches failures] Layout --> Page[page is the routable UI] Page --> URL[/dashboard is reachable]