Pages from Content
By Syncfusion®, Inc. · Last updated
Creating a page for each collection entry, listing pages, pagination with paginate() and the page prop, nested pagination for tags, and references between collections with reference(), getEntry() and getEntries().
Pages from Content
- A collection holds Asha's posts. Now each post needs its own page.
- She also needs a list of all posts, split into pages of 5, and a page for each tag.
- All of these pages are made from the same collection, with dynamic routes.
- In this lesson, you will combine Routing and Content Collections to build a real blog.
Simple lesson: one collection, a few dynamic page files, and Astro builds every blog page for you.
A Real-World Example
- Asha adds a new Markdown file to
src/content/blog. - She does not touch any page file.
- At the next build, the new post gets its own page, appears in the list, and shows up on its tag pages.
Press ▶ Run. Open a post from the list. Then look at the page list in the address bar.
What happened:
getStaticPaths()asked the collection for every post.- Each post became one path: its
idinparams, the whole entry inprops. - The page rendered the entry with
render().
What You Will Learn
In this lesson, you will learn:
- How to build one page per entry.
- How to build a listing page.
- How to split a long list with
paginate(). - How to use the
pageprop and its links. - How to build tag pages, with pagination inside them.
- How to link collections with
reference().
Generating Pages from Content
Creating a Page for Each Entry
- Make a dynamic route file, such as
src/pages/blog/[id].astro. - In
getStaticPaths(), callgetCollection(). - Return one item per entry:
params: { id: post.id },props: { post }. - In the page, read
Astro.props.postandrender()it.
Animation: each entry in the blog collection passes through the [id].astro page file and comes out as its own HTML page.
blog collection
why-astrocollectionsmarkdown-tips
[id].astro getStaticPaths()
/blog/why-astro
/blog/collections
/blog/markdown-tips
One page file, one page per entry.
Building a Listing Page
- A listing page is a normal page, such as
src/pages/blog/index.astro. - It calls
getCollection(), sorts the posts, and links to each one with"/blog/" + post.id. - A listing often shows a short summary. Add a
descriptionfield to the schema for it.
Use the entry's id for the address, so the link on the listing page and the page's real address always match.
Simple lesson: a dynamic route with getStaticPaths() makes the post pages. A normal page lists them.
Pagination
paginate()
- Pagination = splitting a long list over several pages: page 1, page 2, page 3.
getStaticPaths()receives a helper calledpaginate.return paginate(posts, { pageSize: 5 })makes one page per 5 posts.- The file name needs a page parameter, usually a rest parameter:
src/pages/blog/[...page].astro. - Page 1 is at
/blog. Page 2 is at/blog/2, and so on.
The page Prop
Each page gets a prop called page:
page.data= the posts on this page.page.currentPageandpage.lastPage= page numbers.page.total= how many posts in all.page.url.prevandpage.url.next= links to the next and previous pages, orundefined.page.url.firstandpage.url.last= links to the first and last pages.
Press ▶ Run. Seven posts, 3 per page, make 3 pages. Click Next.
page.startis the position of the first item on this page, counting from 0.- In a real blog,
postscomes fromgetCollection(), sorted newest first.
[TS] Typing getStaticPaths with paginate
- Import
GetStaticPathsandPagefromastro:
import type { GetStaticPaths, Page } from "astro";
export const getStaticPaths = (async ({ paginate }) => {
return paginate(["a", "b", "c"], { pageSize: 2 });
}) satisfies GetStaticPaths;
const { page } = Astro.props as { page: Page<string> };
Simple lesson: paginate(list, { pageSize }) in a [...page] route. The page prop holds the items and the links.
Tag Pages and Nested Pagination
A Page per Tag
- Collect every tag from every post, without repeats:
new Set(posts.flatMap((p) => p.data.tags)). - Make one path per tag in
src/pages/tags/[tag].astro. - Pass the matching posts as a prop.
Nested Pagination
- Nested pagination = pagination inside another dynamic route, such as page 2 of the "astro" tag.
- The file is
src/pages/tags/[tag]/[...page].astro. - Call
paginate()once per tag, withparams: { tag }. Then join the results withflatMap().
Press ▶ Run. The tag list links to each tag page. The "astro" tag has more than 2 posts, so it has two pages.
Simple lesson: collect the tags, then paginate once per tag with params: { tag }.
References Between Collections
reference()
- A post has an author. The author's details live in another collection.
- In the schema, write
author: reference("authors"). - In the post's frontmatter, write only the author's ID:
author: asha. - Astro checks that the ID exists, and stores a small reference:
{ collection: "authors", id: "asha" }.
Resolving Referenced Entries
A reference is not the author's data yet. Look it up:
- one reference:
await getEntry(post.data.author), - a list of references:
await getEntries(post.data.related).
- one reference:
A reference keeps data in one place. When Ravi moves city, Asha changes one line in authors.json, and every post shows the new city.
A typo in a referenced ID may only show up when you look it up: getEntry() can return undefined. Use ?. or check for it, so one typo does not break the whole page.
Simple lesson: reference("authors") stores an ID. getEntry() and getEntries() turn it into full entries.
TL;DR
- Page per entry =
[id].astro+getStaticPaths()returningparams: { id }andprops: { post }. - Listing page =
getCollection(), sort, link with"/blog/" + post.id. - paginate() =
paginate(list, { pageSize })in a[...page].astroroute. Page 1 has no number in its address. page prop:
page.data= items on this page,page.currentPage,page.lastPage,page.total,page.url.prev,page.url.next,page.url.first,page.url.last.
- Tag pages = collect tags with a
Set, one path per tag. - Nested pagination =
paginate(tagged, { params: { tag }, pageSize })per tag, joined withflatMap(). - reference("authors") = an ID in the schema. getEntry() / getEntries() = resolve it.
Common mistakes
| Mistake | Wrong idea | Correct idea | What goes wrong |
|---|---|---|---|
params: { slug: post.id } in [id].astro | The param name does not matter | Use the name in the brackets: id | Build error about a missing param |
| Not passing the entry as a prop | The page can find it | Pass props: { post } | post is undefined |
paginate() in [page].astro | Any param works | Use [...page] so page 1 has no number | /blog is missing |
Linking to /blog/1 | Page 1 has a number | Page 1 is at /blog | The link shows the 404 page |
Using post.data.author.name | References hold the data | Call getEntry(post.data.author) first | name is undefined |
| Repeated tags in the tag list | Tags are already unique | Use new Set() | The same tag page is listed twice |
Check Yourself
Exercises
What to Remember
[id].astrowithgetStaticPaths()gives every entry its own page.- A listing page sorts the collection and links with each entry's
id. paginate()splits a list into pages. Thepageprop has the items and the links.- Tag pages collect tags with a
Set. Nested pagination passesparams: { tag }. reference()links collections.getEntry()andgetEntries()resolve the links.
Next: Images and Fonts, where Asha's posts get optimised cover images and a custom font.