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 abooks/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.
- (Optional) We can also add a wildcard directory in addition to the wildcard file, such as
-
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
propsproperty, 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.
- Implementation detail: the structured object has a root
-
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 <>{/*...*/}</>
}