Static และ Dynamic Rendering
ไอเดียในหนึ่งประโยค
หัวข้อที่มีชื่อว่า “ไอเดียในหนึ่งประโยค”Next.js prerender ทุก route เป็น static HTML ตอน build โดย default เว้นแต่ route นั้นต้องใช้ data ตอน request — พอถึงจุดนั้น route จะกลายเป็น dynamic และ render ใหม่ทุก request
Static คือ default
หัวข้อที่มีชื่อว่า “Static คือ default”route ที่ไม่มี input ตอน request จะถูก render ครั้งเดียวตอน build แล้วเสิร์ฟเป็น static HTML ไม่มีอะไรรันต่อ request เลย ทุกคนได้ bytes ชุดเดียวกัน
// app/about/page.tsx — no dynamic APIs, so this is prerendered at build timeexport default function AboutPage() { return <h1>About us</h1>;}นี่คือเส้นทางที่เร็วที่สุด HTML วางอยู่บน CDN แล้วตอบกลับทันที หน้า marketing, docs และ content ส่วนใหญ่ควรอยู่ตรงนี้
อะไรทำให้ route กลายเป็น dynamic
หัวข้อที่มีชื่อว่า “อะไรทำให้ route กลายเป็น dynamic”route จะสลับไปเป็น dynamic rendering ทันทีที่พึ่งพาบางอย่างที่รู้ได้เฉพาะตอน request อย่างใดอย่างหนึ่งต่อไปนี้ทำให้ route flip —
- Dynamic API —
cookies(),headers(),draftMode()หรือการอ่านsearchParams/paramsPromise ตอน request - uncached data request —
fetch()ที่ใส่cache: 'no-store'(หรือ fetch ใด ๆ ภายใต้ default ของ Next.js 15+ ที่คุณยังไม่ opt-in cache) - การ
export const dynamic = 'force-dynamic'แบบตรง ๆ
// app/dashboard/page.tsx — reading cookies() flips this route to dynamicimport { cookies } from 'next/headers';
export default async function Dashboard() { const store = await cookies(); const theme = store.get('theme')?.value ?? 'light'; return <p>Active theme: {theme}</p>;}คุณบังคับ mode เองได้ด้วย segment config export —
// Force a route to render on every request, even with no dynamic APIexport const dynamic = 'force-dynamic';generateStaticParams prerender dynamic segment
หัวข้อที่มีชื่อว่า “generateStaticParams prerender dynamic segment”dynamic segment อย่าง [slug] ไม่มีค่าตายตัวตอน build generateStaticParams บอก Next.js ว่าค่าไหนบ้างที่ต้อง prerender เปลี่ยน dynamic route ให้เป็นชุดของ static page — เป็นตัวแทนของ getStaticPaths เดิมใน App Router
export async function generateStaticParams() { const posts = await fetch('https://api.example.com/posts').then((r) => r.json()); return posts.map((post) => ({ slug: post.slug }));}
export default async function Post({ params }: { params: Promise<{ slug: string }> }) { const { slug } = await params; return <article>Reading post: {slug}</article>;}slug แต่ละตัวที่ return ออกมาจะถูก build เป็น static HTML ไฟล์ของตัวเองตอน build
อ่าน build output
หัวข้อที่มีชื่อว่า “อ่าน build output”next build พิมพ์ legend ที่บอก mode ของทุก route จำสัญลักษณ์สามตัวได้แล้วคุณไม่ต้องเดาอีก —
Route (app) Rendering○ /about Static prerendered as static HTMLƒ /dashboard Dynamic server-rendered on each request● /blog/[slug] SSG prerendered via generateStaticParamsถ้า route ที่คุณคิดว่าเป็น static ขึ้น ƒ แปลว่ามีบางอย่างข้างในไปแตะ data ตอน request ให้ไล่กลับไปหา Dynamic API หรือ uncached fetch ที่เป็นต้นเหตุ
flowchart TD
A[Incoming request] --> B{Dynamic API or uncached fetch?}
B -- No --> C[Static render, prerendered at build]
B -- Yes --> D[Dynamic render, on each request]
E[generateStaticParams] --> C