Skip to main content
generateStaticParams can be used with dynamic route segments to statically generate routes at build time instead of on-demand at request time. It can be exported from:
app/blog/[slug]/page.tsx
export async function generateStaticParams() {
  const posts = await fetch('https://api.example.com/posts').then((res) =>
    res.json()
  )

  return posts.map((post: { slug: string }) => ({
    slug: post.slug,
  }))
}

export default async function Page({
  params,
}: {
  params: Promise<{ slug: string }>
}) {
  const { slug } = await params
  return <h1>{slug}</h1>
}

Parameters

params
object
When multiple dynamic segments in a route each export generateStaticParams, the child function receives the params object generated by the parent. This allows child segments to generate their own params based on parent values.

Returns

An array of objects where each object represents the populated dynamic segments of a single route.
  • Each property key is the segment name.
  • Each property value is the string value (or array of strings for catch-all segments) to fill in.
RouteReturn type
/product/[id]{ id: string }[]
/products/[category]/[product]{ category: string; product: string }[]
/products/[...slug]{ slug: string[] }[]

Good to know

  • Use dynamicParams to control what happens when a dynamic segment not covered by generateStaticParams is visited.
  • Return an empty array (or use export const dynamic = 'force-static') to render all paths at runtime (ISR).
  • During next dev, generateStaticParams is called when you navigate to a route.
  • During next build, it runs before the corresponding Layouts and Pages are generated.
  • During revalidation (ISR), generateStaticParams is not called again.
  • generateStaticParams replaces getStaticPaths from the Pages Router.
  • fetch requests in generateStaticParams are automatically memoized across all generate-prefixed functions, Layouts, Pages, and Server Components.

Examples

Single dynamic segment

app/product/[id]/page.tsx
export function generateStaticParams() {
  return [{ id: '1' }, { id: '2' }, { id: '3' }]
}

// Generates:
// - /product/1
// - /product/2
// - /product/3
export default async function Page({
  params,
}: {
  params: Promise<{ id: string }>
}) {
  const { id } = await params
  return <h1>Product {id}</h1>
}

Multiple dynamic segments

app/products/[category]/[product]/page.tsx
export function generateStaticParams() {
  return [
    { category: 'electronics', product: 'laptop' },
    { category: 'clothing', product: 'shirt' },
  ]
}

export default async function Page({
  params,
}: {
  params: Promise<{ category: string; product: string }>
}) {
  const { category, product } = await params
  return <h1>{category} / {product}</h1>
}

Catch-all segment

app/product/[...slug]/page.tsx
export function generateStaticParams() {
  return [
    { slug: ['electronics', 'laptops'] },
    { slug: ['clothing', 'shirts'] },
  ]
}

export default async function Page({
  params,
}: {
  params: Promise<{ slug: string[] }>
}) {
  const { slug } = await params
  return <h1>{slug.join(' / ')}</h1>
}

All paths at build time

app/blog/[slug]/page.tsx
export async function generateStaticParams() {
  const posts = await fetch('https://api.example.com/posts').then((res) =>
    res.json()
  )
  return posts.map((post: { slug: string }) => ({ slug: post.slug }))
}

Subset of paths, rest at runtime

app/blog/[slug]/page.tsx
export async function generateStaticParams() {
  const posts = await fetch('https://api.example.com/posts').then((res) =>
    res.json()
  )
  // Render only the first 10 posts at build time
  return posts.slice(0, 10).map((post: { slug: string }) => ({ slug: post.slug }))
}

Disabling unspecified paths (404)

app/blog/[slug]/page.tsx
// All slugs not in the list will return 404
export const dynamicParams = false

export async function generateStaticParams() {
  const posts = await fetch('https://api.example.com/posts').then((res) =>
    res.json()
  )
  return posts.slice(0, 10).map((post: { slug: string }) => ({ slug: post.slug }))
}

Multiple dynamic segments (top-down)

Generate parent segment params first, then use them in child generateStaticParams:
app/products/[category]/layout.tsx
export async function generateStaticParams() {
  const products = await fetch('https://api.example.com/products').then(
    (res) => res.json()
  )
  return products.map((p: { category: { slug: string } }) => ({
    category: p.category.slug,
  }))
}
app/products/[category]/[product]/page.tsx
import type { LayoutProps } from 'next'

export async function generateStaticParams({
  params: { category },
}: {
  params: Awaited<LayoutProps<'/products/[category]'>['params']>
}) {
  const products = await fetch(
    `https://api.example.com/products?category=${category}`
  ).then((res) => res.json())

  return products.map((p: { id: string }) => ({ product: p.id }))
}

Version history

VersionChanges
v13.0.0generateStaticParams introduced.

Build docs developers (and LLMs) love