Route Segment Config และ Runtime
ไอเดียในหนึ่งประโยค
หัวข้อที่มีชื่อว่า “ไอเดียในหนึ่งประโยค”Route Segment Config คือชุดของค่า export const ระดับไฟล์ ที่ให้คุณ override ว่า route segment เดียว render, cache และรันบน runtime ไหน
segment config exports
หัวข้อที่มีชื่อว่า “segment config exports”แต่ละตัวเป็น named export อยู่บนสุดของ layout.tsx, page.tsx หรือ route.ts มีผลกับ segment นั้นและลูกของตัวเอง
export const dynamic = 'auto'; // 'auto' | 'force-dynamic' | 'error' | 'force-static'export const revalidate = 3600; // ISR: rebuild this route at most once per hourexport const runtime = 'nodejs'; // 'nodejs' (default) | 'edge' (deprecated)export const fetchCache = 'auto'; // control the default caching of fetch() in this segmentexport const dynamicParams = true; // allow params not returned by generateStaticParamsdynamic— บังคับ mode ของการ render'force-dynamic'render ทุก request ส่วน'force-static'ห้ามใช้ dynamic APIrevalidate— จำนวนวินาทีระหว่างการ rebuild เบื้องหลัง นี่คือวิธีเปิด ISRruntime— สภาพแวดล้อมที่รัน'nodejs'(default) หรือ'edge'(deprecated ใน Next.js 16)fetchCache— override ว่าfetch()ใน segment นี้ถูก cache เป็น default ยังไงdynamicParams— เมื่อเป็นfalseparam ที่ไม่อยู่ในลิสต์ของ generateStaticParams จะคืน 404 แทนที่จะ render ตอน request
Node.js runtime กับ Edge runtime
หัวข้อที่มีชื่อว่า “Node.js runtime กับ Edge runtime”ทุก route รันบน runtime default คือ Node.js ที่ให้ Node API เต็มชุด ส่วน Edge runtime เป็นสภาพแวดล้อมที่เล็กกว่าและมีแต่ Web API ตั้งแต่ Next.js 16 มันถูก deprecate ในฐานะ runtime ของ route — next build จะพิมพ์ The Edge Runtime is deprecated ให้ทุก page ที่ export มัน และ Cache Components ปฏิเสธ export นี้ไปเลย — สำหรับทั้งสองค่า ไม่ใช่แค่ 'edge' (พิสูจน์ด้านล่าง) Edge ยังอยู่ต่อสำหรับ proxy.ts (ไฟล์ที่เดิมเรียกว่า Middleware)
// Opt a route into the Edge runtime — deprecated since Next.js 16: next build warns, Cache Components rejects itexport const runtime = 'edge';ในทางปฏิบัติหมายความว่า —
- Node.js (default) — Node API เต็ม, native module, library ใหญ่ ๆ, database driver และเป็น runtime เดียวที่รองรับ ISR กับ Cache Components ไม่ต้อง export
runtimeเลยก็ได้ตัวนี้ - Edge — ชุด API จำกัด (ไม่มี filesystem, ไม่มี native module, ไม่มี
require) ไม่มี ISR และมี deprecation warning ตอน build ถ้าต้องการ logic แบบ edge เช่น redirect หรือ auth check เบา ๆ ให้ไปไว้ในproxy.tsแทนที่จะอยู่บน route
ISR ผ่าน revalidate
หัวข้อที่มีชื่อว่า “ISR ผ่าน revalidate”Incremental Static Regeneration ทำให้ route ยัง static และเร็ว แต่ยอมให้ content stale ได้แล้ว refresh ตามเวลา ตั้ง revalidate เป็นจำนวนวินาที Next.js จะเสิร์ฟ static page ที่ cache ไว้ แล้ว rebuild เบื้องหลังหลังพ้นช่วงนั้น
export const revalidate = 60;
export default async function Products() { const products: { id: number; name: string }[] = await fetch('https://api.example.com/products').then((r) => r.json()); return <ul>{products.map((p) => <li key={p.id}>{p.name}</li>)}</ul>;}request แรกหลังพ้น 60 วินาทีจะ trigger การ rebuild เบื้องหลัง ผู้เข้าชมยังได้ page ที่ cache ไว้แบบเร็วต่อไปจนกว่าอันใหม่จะพร้อม
ภายใต้ cacheComponents: true export revalidate จะถูกปฏิเสธ ตัวแทนคือ scope 'use cache' พร้อม profile ของ cacheLife — จากนั้น next build จะแสดง route เป็น ○ /products 1m 1h —
import { cacheLife } from 'next/cache';
export default async function Products() { 'use cache'; cacheLife('minutes'); // stale 5m, revalidate 1m, expire 1h — closest preset to revalidate = 60 const products: { id: number; name: string }[] = await fetch('https://api.example.com/products').then((r) => r.json()); return <ul>{products.map((p) => <li key={p.id}>{p.name}</li>)}</ul>;}export ทุกตัว พิสูจน์กับ build จริง
หัวข้อที่มีชื่อว่า “export ทุกตัว พิสูจน์กับ build จริง”แต่ละ export ที่เข้ากันไม่ได้ให้ error รูปแบบเดียวกันเป๊ะ ๆ ภายใต้ cacheComponents: true — ตรวจสอบทีละตัวใน probe จริง ตัด path ไฟล์ออกเท่านั้น —
Error: Route segment config "dynamic" is not compatible with `nextConfig.cacheComponents`. Please remove it.Error: Route segment config "revalidate" is not compatible with `nextConfig.cacheComponents`. Please remove it.Error: Route segment config "fetchCache" is not compatible with `nextConfig.cacheComponents`. Please remove it.Error: Route segment config "dynamicParams" is not compatible with `nextConfig.cacheComponents`. Please remove it.Error: Route segment config "runtime" is not compatible with `nextConfig.cacheComponents`. Please remove it.Error: Route segment config "experimental_ppr" is not compatible with `nextConfig.cacheComponents`. Please remove it.preferredRegion กับ maxDuration ไม่อยู่ใน list นั้น — build ด้วย cacheComponents: true พร้อม export ทั้งสองตัวอยู่ด้วย build ผ่าน preferredRegion พิมพ์ deprecation warning ของตัวเองที่ไม่เกี่ยวกัน (มัน deprecated ทุกที่ ไม่ว่าจะมี Cache Components หรือไม่) maxDuration build เงียบ ๆ ไม่มี warning เลย —
⚠ The "preferredRegion" route segment config is deprecated. Learn more: https://nextjs.org/docs/messages/preferred-region-deprecated| Export | ภายใต้ Cache Components | ตัวแทน |
|---|---|---|
dynamic = 'force-dynamic' | ปฏิเสธ — build error | ไม่ต้องใช้แล้ว ทุก route เป็น dynamic โดย default อยู่แล้ว ห่อส่วนที่ต้อง prerender ด้วย 'use cache' แทน |
dynamic = 'force-static' | ปฏิเสธ — build error | 'use cache' พร้อม cacheLife ยาว ๆ (เช่น 'max') สำหรับ data ที่ไม่ cache ย้าย request-time API ไปไว้ใน <Suspense> boundary |
revalidate | ปฏิเสธ — build error | cacheLife(...) ใน scope 'use cache' — เลือก profile ที่ใกล้ที่สุด ('seconds'…'max') หรือ define custom profile เอง |
fetchCache | ปฏิเสธ — build error | ไม่ต้องใช้แล้ว — ทุก fetch() ใน scope 'use cache' ถูก cache อัตโนมัติ |
dynamicParams = false | ปฏิเสธ — build error | เรียก notFound() ในหน้า page เมื่อ param ที่ไม่ถูก return จาก generateStaticParams ไม่ตรงกับ data จริง |
runtime ('nodejs' หรือ 'edge') | ปฏิเสธ — build error ทั้งสองค่า | ไม่มี — export นี้ไม่รองรับเลย Cache Components ต้องใช้ Node.js runtime ไม่มีอะไรให้ config อีกแล้ว |
preferredRegion | build ผ่าน — มีแค่ deprecation warning ที่ไม่เกี่ยวกัน | ไม่มี — deprecated แยกจาก Cache Components เอาออกเลยดีกว่า แล้วปล่อยให้ deployment platform เลือก region เอง |
maxDuration | build ผ่าน — ไม่มี warning | ไม่ต้องใช้ตัวแทนเลย มันเป็น hint ของ deployment platform เรื่อง execution time ไม่เกี่ยวกับ caching เลยคงเดิมทุกอย่าง |
experimental_ppr = true | ปฏิเสธ — build error | ไม่ต้องใช้แล้ว PPR คือ behavior default ทันทีที่ตั้ง cacheComponents: true |
flowchart TD A[Route segment config] --> B[dynamic] A --> C[revalidate for ISR] A --> D[runtime] D --> E[nodejs, full APIs, default] D --> F[edge, deprecated in 16, limited APIs, no ISR]
Mental Model
หัวข้อที่มีชื่อว่า “Mental Model”Route Segment Config คือชุดปุ่มหมุนที่อยู่ ข้างนอก ไฟล์ — คุณไม่เคยต้องแตะ render body เลยเพื่อเปลี่ยน caching หรือ runtime แค่เพิ่ม export const ไว้บนสุด Cache Components เอาปุ่มหมุนพวกนี้ออกเกือบหมด เหลือแค่สองตัว (preferredRegion, maxDuration ทั้งคู่เป็น deployment hint ที่ไม่เกี่ยวกับ caching เลย) แล้วแทนที่ที่เหลือด้วยการตัดสินใจที่อยู่ ข้างใน ตัว function แทน — 'use cache' กับ cacheLife(...) อยู่ติดกับโค้ดที่มันมีผล แทนที่จะประกาศ policy ทั้งไฟล์แบบ blanket จากข้างนอก
ข้อแลกเปลี่ยน
หัวข้อที่มีชื่อว่า “ข้อแลกเปลี่ยน”| สิ่งที่ได้ | ประโยชน์ | ต้นทุน |
|---|---|---|
| Route Segment Config (โมเดลเดิม) | หนึ่งบรรทัดต่อหนึ่งเรื่อง ประกาศไว้บนสุดของไฟล์ อ่านง่าย | เป็น setting แบบ blanket ทั้งไฟล์ — cache fetch สองตัวใน segment เดียวกันต่างกันไม่ได้ |
'use cache' + cacheLife (Cache Components) | การตัดสินใจเรื่อง caching อยู่ติดกับโค้ดที่มันมีผล ที่ระดับความละเอียดไหนก็ได้ตามใจ (function, component, page) | ต้องอ่านเพิ่มข้างในตัว function body ไม่มี summary รวมท้ายไฟล์ว่า segment นั้น caching policy อะไร |
ไม่ export runtime เลย | ได้ Node.js API เต็มชุด เข้ากันได้กับ Cache Components | opt-in Edge ที่ cold-start เร็วกว่าไม่ได้บน route (Edge ก็ deprecated อยู่แล้ว) |
เอา preferredRegion/maxDuration ออกเมื่อไม่ได้ใช้ | ลด config surface ที่ต้อง sync กับ deployment จริงไปหนึ่งอย่าง | ไม่มีเลย — สองตัวนี้ไม่เคยขัดกับ Cache Components ตั้งแต่แรก |
ความเข้าใจผิดที่พบบ่อย
หัวข้อที่มีชื่อว่า “ความเข้าใจผิดที่พบบ่อย”- Cache Components ปฏิเสธแค่
runtime = 'edge'เพราะเป็นค่าที่ deprecated มันปฏิเสธ exportruntimeเองเลย ไม่ว่าจะค่าไหน —runtime = 'nodejs'ก็ล้มเหมือนกันเป๊ะ ๆ เพราะ Cache Components ต้องใช้ Node.js อยู่แล้ว ไม่มีอะไรให้ config เหลืออีก preferredRegionกับmaxDurationก็ต้อง migrate ไปใช้'use cache'/cacheLifeด้วย ไม่ใช่ caching config เลย — เป็น deployment-platform hint (region, execution time limit) Cache Components ไม่แตะมันเลยpreferredRegiondeprecated ด้วยเหตุผลอื่น ส่วนmaxDurationไม่ได้ deprecated เลยด้วยซ้ำdynamicParams = falseไม่มีตัวแทนภายใต้ Cache Components มีอยู่ — เรียกnotFound()ในหน้า page เองเมื่อ param ที่ไม่อยู่ใน list ไม่ตรงกับ data จริง แทนที่จะประกาศ behavior 404 เป็น file-level export แบบ blanket- เอา
experimental_ppr = trueออกแปลว่าปิด PPR ตรงข้ามเลย — export นั้น (และ config flagexperimental.pprที่คู่กัน) ถูกเอาออกเพราะ PPR กลายเป็น behavior default ของcacheComponents: true— ไม่มีทางเปิดcacheComponentsโดยไม่มี PPR ทำงานอยู่ด้วย
อ่านโค้ดที่ AI เขียน
หัวข้อที่มีชื่อว่า “อ่านโค้ดที่ AI เขียน”เมื่อ snippet ที่ generate มา export Route Segment Config ให้เช็กสิ่งเหล่านี้ก่อนเชื่อมัน —
- project มี
cacheComponents: trueในnext.config.tsแล้ว แต่ page ยัง exportdynamic,revalidate,fetchCache,dynamicParams,runtimeหรือexperimental_pprอยู่หรือเปล่า? ทุกตัวทำให้ build ล้มตรง ๆ — นี่คือ signal ตอน build ไม่ใช่ bug ที่ subtle แต่โค้ดที่ AI generate มามักเขียนแบบนี้ (หรือ copy จาก example เก่า) โดยไม่ได้เช็ก config จริงของ project - โค้ดตั้ง
runtime = 'nodejs'ตรง ๆ โดยคิดว่าปลอดภัยเสมอเพราะเป็นค่า default หรือเปล่า? ภายใต้ Cache Components การ export มันเลยแม้จะเป็นค่า default ก็ถูกปฏิเสธ dynamicParams = falseโผล่มาที่ไหนใน project ที่ใช้ Cache Components หรือเปล่า? มันต้องกลายเป็นการเรียกnotFound()ข้างใน component แทนpreferredRegionหรือmaxDurationกำลังถูก “migrate” ไปใช้cacheLifeหรือ'use cache'โดยไม่จำเป็นหรือเปล่า ทั้งที่ทั้งคู่ไม่ได้เข้ากันไม่ได้กับ Cache Components เลย?- documentation เก่าหรือ example ที่ค้างอยู่ยังอ้างถึง
experimental.pprหรือexperimental_pprเหมือนว่าต้องตั้งเพื่อให้ PPR ทำงานใน Next.js 16 หรือเปล่า? ทั้งคู่ถูกเอาออกแล้ว — PPR เปิดอยู่ทุกครั้งที่cacheComponents: true
🐛 หา bug ในโค้ดนี้
import type { NextConfig } from 'next'
const nextConfig: NextConfig = {cacheComponents: true,}
export default nextConfig
// app/dashboard/page.tsxexport const dynamic = 'force-dynamic'
export default function Dashboard() {return <div>Dashboard</div>}เฉลย
cacheComponents: true กับ export const dynamic = 'force-dynamic' อยู่ด้วยกันไม่ได้ build ล้มด้วย Route segment config "dynamic" is not compatible with nextConfig.cacheComponents. Please remove it. — ภายใต้ Cache Components ทุก route เป็น dynamic โดย default อยู่แล้ว ดังนั้น force-dynamic ไม่ใช่แค่ซ้ำซ้อน มันคือ export ที่ถูกปฏิเสธไปเลย ทางแก้คือลบ export dynamic ทิ้งทั้งหมด ถ้าส่วนไหนของหน้าต้องการ static หรือ cache จริง ๆ ให้ห่อส่วนนั้นด้วย 'use cache' แทนที่จะประกาศ mode ทั้งไฟล์จากข้างนอก component