Build pages programmatically

programmatic pages : pages that don't match a route file

Route files hardcode a page, one that match their name: one page for one file.

Sometimes, we want to create pages programmatically.

from slugs to pages

The goal is to build pages programmatically, from a data-source, that provides slugs for the pages along with their content. The data source can be any data: local files or data received from a server.

We determine:

  • a list of pages
  • where they stand in the URL
  • their content
  • which React component they use.

pipeline overview

  • We determine where the generated pages live in the URL. If we want them to live scoped under the books/ path, we create a books/ directory.

  • We then create a (wildcard) React file under that directory, with a special name that uses brackets, such as [book].tsx, to signal a special page.

    • (Optional) We can also add a wildcard directory in addition to the wildcard file, such as [book]/[chapter].tsx. In this scenario, we generate a list of books programmatically, each one have its own list of chapters generated programmatically.
  • In this file, we read the data-source to determine a list of slugs that we are to make pages for. For example, we read a list of markdown files and compute a list of slugs from them. We register the slug list.

  • Then, we establish a data-gathering function that fetches data according to a slug, and returns the data. We must return the data in a specific, structured format. For example, we reads a markdown file and returns its content in a structured object.

    • Implementation detail: the structured object has a root props property, which itself is an object as well, and as its name implies, hold a series of props, each one being given to the template React component as a React prop.
  • Finally, we default export a React component to make it become the template React component. It receives the gathered data . It can set <title> in <Head> to ensure the generated page has an appropriate title.

other details

  • The pipeline runs at build-time.
  • Next.js ensures there is a fully-fledged HTML page for each slug.

determine and provide the slugs

What Next.js expects: an array of paths (paths), each member being a path and about to receive its own page.

Each path has a params object, which comes with the slug(s).

The single slug scenario is when the path is made of a single slug. We provide the slug, keyed to the wildcard file name, such as book for [book].tsx.

//  [
//    { params: { book: "typescript"}},
//    { params: { book: "javascript"}},
//    { params: { book: "clang"}}
//  ]

Note: if we provide several slugs in the params object, it denotes that at least one slug is to match a wildcard directory, in addition to the slug matching the wildcard file.

provide the slugs (implementation example)

[book].tsx

export function getStaticPaths() {
  	// make use of a data source, usually an array
    const books = getBooks() 

    // build the array of paths, here by looping over the data source array
    const paths = books.map((book) => ({
        params: { book: book.slug },
    }))
		
    // return it, along with options
    return { paths, fallback: false }
}

gather data for a given slug

getStaticProps is expected to return this shape, with props being the main content, a series of props given to the React component:

// {
//     props: {
//         book: {
//             title: book.title,
//             slug: book.slug,
//             author: book.author,
//             lastUpdate: book.lastUpdate,
//         },
//     },
// )

[book].tsx

export async function getStaticProps({ params }: { params: { book: string } }): Promise<{
    props: BookUIProps
}> {
  	// 1.0 get slug from params (book name)
    const bookSlug = params.book
    
    // 2.0 fetch data according to the slug (book content)
    const books = getBooks()
    const book = books.find((b) => b.slug === bookSlug)
    if (!book) throw new Error(`Book not found: ${bookSlug}`)
    const data = book.data
		
    // 3.0 returned the object with props
    return {
        props: {
            book: {
                title: book.title,
                slug: book.slug,
                author: book.author,
                lastUpdate: book.lastUpdate,
            },
        },
    }
}

make use of the data

[book].tsx

export default function BookUI({ book }) {
    const { title, slug, author, lastUpdate } = book
    return <>{/*...*/}</>
}
earlymorning logo

© Antoine Weber 2026 - All rights reserved

Build pages programmatically

programmatic pages : pages that don't match a route file

Route files hardcode a page, one that match their name: one page for one file.

Sometimes, we want to create pages programmatically.

from slugs to pages

The goal is to build pages programmatically, from a data-source, that provides slugs for the pages along with their content. The data source can be any data: local files or data received from a server.

We determine:

  • a list of pages
  • where they stand in the URL
  • their content
  • which React component they use.

pipeline overview

  • We determine where the generated pages live in the URL. If we want them to live scoped under the books/ path, we create a books/ directory.

  • We then create a (wildcard) React file under that directory, with a special name that uses brackets, such as [book].tsx, to signal a special page.

    • (Optional) We can also add a wildcard directory in addition to the wildcard file, such as [book]/[chapter].tsx. In this scenario, we generate a list of books programmatically, each one have its own list of chapters generated programmatically.
  • In this file, we read the data-source to determine a list of slugs that we are to make pages for. For example, we read a list of markdown files and compute a list of slugs from them. We register the slug list.

  • Then, we establish a data-gathering function that fetches data according to a slug, and returns the data. We must return the data in a specific, structured format. For example, we reads a markdown file and returns its content in a structured object.

    • Implementation detail: the structured object has a root props property, which itself is an object as well, and as its name implies, hold a series of props, each one being given to the template React component as a React prop.
  • Finally, we default export a React component to make it become the template React component. It receives the gathered data . It can set <title> in <Head> to ensure the generated page has an appropriate title.

other details

  • The pipeline runs at build-time.
  • Next.js ensures there is a fully-fledged HTML page for each slug.

determine and provide the slugs

What Next.js expects: an array of paths (paths), each member being a path and about to receive its own page.

Each path has a params object, which comes with the slug(s).

The single slug scenario is when the path is made of a single slug. We provide the slug, keyed to the wildcard file name, such as book for [book].tsx.

//  [
//    { params: { book: "typescript"}},
//    { params: { book: "javascript"}},
//    { params: { book: "clang"}}
//  ]

Note: if we provide several slugs in the params object, it denotes that at least one slug is to match a wildcard directory, in addition to the slug matching the wildcard file.

provide the slugs (implementation example)

[book].tsx

export function getStaticPaths() {
  	// make use of a data source, usually an array
    const books = getBooks() 

    // build the array of paths, here by looping over the data source array
    const paths = books.map((book) => ({
        params: { book: book.slug },
    }))
		
    // return it, along with options
    return { paths, fallback: false }
}

gather data for a given slug

getStaticProps is expected to return this shape, with props being the main content, a series of props given to the React component:

// {
//     props: {
//         book: {
//             title: book.title,
//             slug: book.slug,
//             author: book.author,
//             lastUpdate: book.lastUpdate,
//         },
//     },
// )

[book].tsx

export async function getStaticProps({ params }: { params: { book: string } }): Promise<{
    props: BookUIProps
}> {
  	// 1.0 get slug from params (book name)
    const bookSlug = params.book
    
    // 2.0 fetch data according to the slug (book content)
    const books = getBooks()
    const book = books.find((b) => b.slug === bookSlug)
    if (!book) throw new Error(`Book not found: ${bookSlug}`)
    const data = book.data
		
    // 3.0 returned the object with props
    return {
        props: {
            book: {
                title: book.title,
                slug: book.slug,
                author: book.author,
                lastUpdate: book.lastUpdate,
            },
        },
    }
}

make use of the data

[book].tsx

export default function BookUI({ book }) {
    const { title, slug, author, lastUpdate } = book
    return <>{/*...*/}</>
}