ข้ามไปยังเนื้อหา

Route Segment Config และ Runtime

Route Segment Config คือชุดของค่า export const ระดับไฟล์ ที่ให้คุณ override ว่า route segment เดียว render, cache และรันบน runtime ไหน

แต่ละตัวเป็น named export อยู่บนสุดของ layout.tsx, page.tsx หรือ route.ts มีผลกับ segment นั้นและลูกของตัวเอง

app/blog/page.tsx
export const dynamic = 'auto'; // 'auto' | 'force-dynamic' | 'error' | 'force-static'
export const revalidate = 3600; // ISR: rebuild this route at most once per hour
export const runtime = 'nodejs'; // 'nodejs' (default) | 'edge' (deprecated)
export const fetchCache = 'auto'; // control the default caching of fetch() in this segment
export const dynamicParams = true; // allow params not returned by generateStaticParams
  • dynamic — บังคับ mode ของการ render 'force-dynamic' render ทุก request ส่วน 'force-static' ห้ามใช้ dynamic API
  • revalidate — จำนวนวินาทีระหว่างการ rebuild เบื้องหลัง นี่คือวิธีเปิด ISR
  • runtime — สภาพแวดล้อมที่รัน 'nodejs' (default) หรือ 'edge' (deprecated ใน Next.js 16)
  • fetchCache — override ว่า fetch() ใน segment นี้ถูก cache เป็น default ยังไง
  • dynamicParams — เมื่อเป็น false param ที่ไม่อยู่ในลิสต์ของ generateStaticParams จะคืน 404 แทนที่จะ render ตอน request

ทุก 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 it
export 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

Incremental Static Regeneration ทำให้ route ยัง static และเร็ว แต่ยอมให้ content stale ได้แล้ว refresh ตามเวลา ตั้ง revalidate เป็นจำนวนวินาที Next.js จะเสิร์ฟ static page ที่ cache ไว้ แล้ว rebuild เบื้องหลังหลังพ้นช่วงนั้น

app/products/page.tsx
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 —

app/products-cc/page.tsx
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 ที่เข้ากันไม่ได้ให้ 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 errorcacheLife(...) ใน 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 อีกแล้ว
preferredRegionbuild ผ่าน — มีแค่ deprecation warning ที่ไม่เกี่ยวกันไม่มี — deprecated แยกจาก Cache Components เอาออกเลยดีกว่า แล้วปล่อยให้ deployment platform เลือก region เอง
maxDurationbuild ผ่าน — ไม่มี 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]
Route segment config กับ runtime ทั้งสอง

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 Componentsopt-in Edge ที่ cold-start เร็วกว่าไม่ได้บน route (Edge ก็ deprecated อยู่แล้ว)
เอา preferredRegion/maxDuration ออกเมื่อไม่ได้ใช้ลด config surface ที่ต้อง sync กับ deployment จริงไปหนึ่งอย่างไม่มีเลย — สองตัวนี้ไม่เคยขัดกับ Cache Components ตั้งแต่แรก
  • Cache Components ปฏิเสธแค่ runtime = 'edge' เพราะเป็นค่าที่ deprecated มันปฏิเสธ export runtime เองเลย ไม่ว่าจะค่าไหน — runtime = 'nodejs' ก็ล้มเหมือนกันเป๊ะ ๆ เพราะ Cache Components ต้องใช้ Node.js อยู่แล้ว ไม่มีอะไรให้ config เหลืออีก
  • preferredRegion กับ maxDuration ก็ต้อง migrate ไปใช้ 'use cache'/cacheLife ด้วย ไม่ใช่ caching config เลย — เป็น deployment-platform hint (region, execution time limit) Cache Components ไม่แตะมันเลย preferredRegion deprecated ด้วยเหตุผลอื่น ส่วน maxDuration ไม่ได้ deprecated เลยด้วยซ้ำ
  • dynamicParams = false ไม่มีตัวแทนภายใต้ Cache Components มีอยู่ — เรียก notFound() ในหน้า page เองเมื่อ param ที่ไม่อยู่ใน list ไม่ตรงกับ data จริง แทนที่จะประกาศ behavior 404 เป็น file-level export แบบ blanket
  • เอา experimental_ppr = true ออกแปลว่าปิด PPR ตรงข้ามเลย — export นั้น (และ config flag experimental.ppr ที่คู่กัน) ถูกเอาออกเพราะ PPR กลายเป็น behavior default ของ cacheComponents: true — ไม่มีทางเปิด cacheComponents โดยไม่มี PPR ทำงานอยู่ด้วย

เมื่อ snippet ที่ generate มา export Route Segment Config ให้เช็กสิ่งเหล่านี้ก่อนเชื่อมัน —

  • project มี cacheComponents: true ใน next.config.ts แล้ว แต่ page ยัง export dynamic, 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 ในโค้ดนี้

next.config.ts
import type { NextConfig } from 'next'
const nextConfig: NextConfig = {
cacheComponents: true,
}
export default nextConfig
// app/dashboard/page.tsx
export 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

ค่า Route Segment Config อยู่ที่ไหน?
export ตัวไหนเปิด Incremental Static Regeneration?
runtime default ของ route คืออะไร?
สถานะของ export const runtime = 'edge' ใน Next.js 16 คืออะไร?
build ผ่านไหม?
// next.config.ts
const nextConfig = { cacheComponents: true }
// app/page.tsx
export const runtime = 'nodejs'
export Route Segment Config สองตัวไหนที่ build ผ่านภายใต้ cacheComponents: true โดยไม่ต้องมีตัวแทนใน Cache Components เลย?
ตัวแทนที่เข้ากันได้กับ Cache Components ของบรรทัดนี้คืออะไร?
export const dynamicParams = false

export default function Page() { /* ... */ }