Astro: Collection Pagination Using Rest Parameter Dynamic Route
Astro has built-in pagination to split collections into multiple pages, with each page containing a specified number of entries. This feature works with dynamic routes, allowing for URLs like /blog/
, /blog/2/
, /blog/3/
, and so on.
Many examples of Astro pagination can be found in the Astro documentation and other online sources. However, I encountered an issue where I kept receiving the error Property 'data' does not exist on type 'never'
when trying to access page
properties. This error suggests that the page
variable was not properly typed as Page
. I could manually apply the Page
type to the page
variable to fix the error, but that didn’t seem like the right solution.
The following solution fixes the type issue by importing the GetStaticPaths
type and using the satisfies
operator. The code provides a basic post listing for the current page as well as simple page navigation. You will need to add formatting and styles as appropriate.
Astro Project Structure
In this demonstration, the Astro project structure is as follows: The collection, named blog
, contains three entries. There is also a dynamic route called [...page].astro
which contains all of the code provided below.
AstroProject/
└── src/
├── content/
│ └── blog/
│ ├── post1.mdx
│ ├── post2.mdx
│ └── post3.mdx
└── pages/
└── blog/
├── [...slug].astro
└── [...page].astro
Pagination Using Dynamic Route
In the [...page].astro
file, add the following code segment to the codefence. Adjust the collection name as needed.
Key parts of the code are:
import type { GetStaticPaths } from 'astro';
– Imports helper types to work with results returned bygetStaticPaths
in dynamic routes.satisfies GetStaticPaths
– Validates or type checks the result without changing the type.
The pageSize
is set to 10
entries per page. With three entries in the collection, setting pageSize
to 1
creates three pages.
---
import { getCollection } from 'astro:content';
import type { GetStaticPaths } from 'astro';
export const getStaticPaths = (async ({paginate}) => {
const posts = (await getCollection('blog'))
.sort((a, b) => b.data.pubDate.valueOf() - a.data.pubDate.valueOf());
return paginate(posts, { pageSize: 10 } );
}) satisfies GetStaticPaths;
const { page } = Astro.props;
const pageNums = Array.from({length: page.lastPage}, (_, i) => i + 1);
---
Listing Current Page Entries
Add the following to [...page].astro
to access and list each entry or post for the current page.
{
page.data.map((post) => (
<a href={`/blog/${post.slug}/`}>{post.data.title}</a>
))
}
Adding Page Navigation
Include the following in [...page].astro
to add page navigation links, such as “previous page,” “next page,” and numbered pages.
An extra condition ensures that the first page links to /blog/
instead of /blog/1/
.
{
(page.lastPage > 1) && (
<nav>
{ page.url.prev && ( <a href={page.url.prev}>Previous Page</a> ) }
{ pageNums.map((num) => ( <a href={`/blog/${num === 1 ? '' : (num) + '/'}`}>{num}</a> ) ) }
{ page.url.next && ( <a href={page.url.next}>Next Page</a> ) }
</nav>
)
}
Summary
Astro’s built-in pagination feature splits collections into multiple pages and works with dynamic routes, creating URLs like /blog/
, /blog/2/
, etc. The typing issue with the page
variables was resolved by importing GetStaticPaths
types and using the satisfies
operator. With a few lines of code, it is relatively easy to provide paginated post listings and page navigation.