Build pages from a data-source
from slugs to pages
The aim is to build pages from a data-source other than source-code files. The data source may be some local files or some data served by a remote server. We determine a list of pages, their content, where should they exist in the URL, and which React component they use.
pipeline overview
The pipeline runs at build-time. The whole process may be described as so:
- We determine on which route the generated pages will live. We create a React file at the correct location in the file tree, and we give it a special name, such as
[chapter].tsxto signal a special page. - We read the data-source to determine and register a list of slugs that we are about to make pages from. For example, we read a list of markdown files and determine a slug for each, and we register the list.
- Then, we establish a data-gathering function that fetches data according to the provided slug, and returns it as a structured object. For example, it reads a markdown file and returns its content in a structured object.
- Implementation detail: Such object has a
propsproperty, which itself is an object as well, and each property it holds is set to be given to the designated React component as a regular React prop.
- Implementation detail: Such object has a
- Finally, we designate a React component to be used as a template, as it receives the gathered data and make use of it. It may notably set
<title>in<Head>to ensure the generated page has an appropriate title. - Next.js ensures there is a fully-fledged HTML page for each slug.
determine the slugs
What is expected: an array of objects, each one representing a page. Each page comes with a params object. This object comes with one or more slugs. The single slug scenario is the most simple to reason about. We provide the slug as a property, whose name may be semantic or not, but the property name has to match the file, e.g. book for [book].tsx.
// [
// { params: { book: "typescript"}},
// { params: { book: "javascript"}},
// { params: { book: "clang"}}
// ]
[book].tsx
export function getStaticPaths() {
const books = getBooks()
const paths = books.map((book) => ({
params: { book: book.slug },
}))
return { paths, fallback: false }
}
gather data for a given slug
getStaticProps is expected to return this kind of structure:
// {
// 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
}> {
const { book: bookSlug } = params
const books = getBooks()
const book = books.find((b) => b.slug === bookSlug)
if (!book) {
throw new Error(`Book not found: ${bookSlug}`)
}
const data = book.data // gather data here
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 <>{/*...*/}</>
}