跳轉至

建立 2026-09-16 更新 2026-09-16

動態路由

商品頁、文章頁這類網址常常帶著一個會變動的識別碼,例如 /products/42/blog/hello-world。Next.js 用方括號資料夾名稱表示這種「會變動的路徑區段」,不用為每一種可能的值各寫一個頁面。

基本用法

app/
└── products/
    └── [id]/
        └── page.tsx        # /products/1、/products/2 ...都會對應到這個檔案

方括號裡的名稱會變成 params 物件的欄位,在頁面元件裡可以直接取用:

app/products/[id]/page.tsx
export default async function ProductPage({
  params,
}: {
  params: Promise<{ id: string }>;
}) {
  const { id } = await params;
  return <h1>商品編號{id}</h1>;
}

params 是 Promise

Next.js 15 起,params(與 searchParams)改為非同步,需要 await 才能取得實際內容;沿用舊版寫法(直接當物件用)在建置時會出現警告。

三種區段語法

語法 範例路徑 說明
[id] /products/42 剛好一段、必填的動態區段。
[...slug] /docs/a/b/c Catch-all,把剩下所有區段收進一個陣列,且至少要有一段
[[...slug]] /docs/docs/a/b 可選的 catch-all,連完全沒有子路徑(只到 /docs)都會符合。

Catch-all 的 params 會是陣列,例如 /docs/a/b/c 對應到 { slug: ['a', 'b', 'c'] }

搭配 generateStaticParams 預先產生頁面

如果動態頁面的內容在建置時就能知道所有可能的值(例如部落格文章清單),可以用 generateStaticParams 讓 Next.js 在建置階段就把每個頁面預先產生成靜態 HTML,兼顧動態路由的彈性與靜態頁面的效能:

app/blog/[slug]/page.tsx
export async function generateStaticParams() {
  const posts = await getPosts();
  return posts.map((post) => ({ slug: post.slug }));
}

export default async function BlogPost({
  params,
}: {
  params: Promise<{ slug: string }>;
}) {
  const { slug } = await params;
  const post = await getPost(slug);
  return <article>{post.title}</article>;
}

沒有列在 generateStaticParams 回傳結果裡的路徑,預設仍會在第一次被造訪時動態渲染。

下一步

會建立路徑之後,接著看使用者如何在頁面之間移動,詳見 導覽與連結