When I was building the MCP directory for Free DevTools, I ran into a problem: loading 13,000+ repositories from JSON at once was painfully slow.
I initially tried to build a custom pagination component, but it didnāt work efficiently ā everything had to load at once before the UI even appeared.
Thatās when I started digging into Astroās built-in pagination system ā and it turned out to be the perfect solution for large collections.
The Problem: Heavy Data Rendering
If youāve ever fetched a big dataset (thousands of items) and tried to render it in one go, youāve seen what happens:
- The page freezes or crashes during hydration.
- Memory spikes.
- Scrolling becomes laggy.
- Static build times increase drastically.
In my case, the MCP directory had 13k+ items coming from JSON. I needed pagination that could:
- Split data into multiple pages.
- Load only whatās needed.
- Still be SEO-friendly (pre-rendered static pages).
Enter Astro Pagination
Astro has first-class support for pagination, directly built into its file-based routing system.
You donāt need a client-side library or heavy runtime logic ā itās done at build time.
You can read the full guide here: Astro Pagination Docs
Basic Pagination Setup
To paginate data, you need a file under src/pages/
with [page].astro
in its name.
Example:src/pages/mcp/[page].astro
This tells Astro to automatically generate:
/mcp/1
/mcp/2
/mcp/3
...
Example Code
---
export async function getStaticPaths({ paginate }) {
// Fetch or import your large dataset
const response = await fetch("https://api.example.com/data");
const data = await response.json();
// Each page will show 20 items
return paginate(data.items, { pageSize: 20 });
}
const { page } = Astro.props;
---
{page.data.map((item) => (
- {item.name}
))}
Thatās it. Astro automatically:
- Slices the data into chunks of
pageSize
. - Builds each page statically.
- Provides navigation URLs (
page.url.prev
,page.url.next
, etc.).
Items per Page
You control how many items appear per page using the pageSize
option in paginate()
.
Example:
return paginate(data, { pageSize: 10 });
That will generate:
-
/1
ā items 1ā10 -
/2
ā items 11ā20 -
/3
ā items 21ā30, and so on.
For my MCP project, I used 50 items per page to balance between page size and build speed.
Page Prop Explained
Every generated page receives a page
prop with all the info you need.
Property | Description |
---|---|
page.data |
Array of items for this page |
page.currentPage |
Current page number |
page.lastPage |
Total number of pages |
page.size |
Number of items per page |
page.total |
Total items in all pages |
page.url.current |
URL of the current page |
page.url.next |
URL of next page (if available) |
page.url.prev |
URL of previous page (if available) |
page.url.first |
URL of first page |
page.url.last |
URL of last page |
Example usage:
Showing {page.start + 1}ā{page.end} of {page.total} items
Navigation Links (Next/Prev/First/Last)
You can easily build full navigation using the provided URLs:
Astro only generates the relevant links, so no need to manually check page numbers.
Nested Pagination (Optional)
You can even combine pagination with other dynamic routes.
Example:
If you want paginated lists grouped by category, create /src/pages/webdev/[page].astro
.
---
export async function getStaticPaths({ paginate }) {
const categories = ["frontend", "backend"];
const allItems = await fetchItems();
return categories.flatMap((category) => {
const filtered = allItems.filter((item) => item.category === category);
return paginate(filtered, {
params: { category },
pageSize: 10,
});
});
}
const { page } = Astro.props;
const { category } = Astro.params;
---
{page.data.map((item) => - {item.name}
)}
This generates:
/frontend/1
/frontend/2
/backend/1
/backend/2
Why Itās Better
ā
Fully static ā great for SEO.
ā
No client-side pagination logic or JS required.
ā
Easy to maintain, no state management.
ā
Works perfectly with Markdown collections, APIs, or JSON files.
For my MCP directory, moving from client-side pagination to Astroās native pagination made the site load much faster and lighter. Each page now loads instantly and only processes a small chunk of data.
Conclusion
Astroās pagination is simple, fast, and built for static scalability.
Whether youāre handling Markdown posts, API responses, or JSON data ā it gives you a clean way to manage large datasets without overloading the browser.
If youāre building something like my Free DevTools MCP directory, give Astroās pagination a try.
Itās one of those small features that completely changes how efficiently you can build large-scale static pages.
Any feedback or contributors are welcome!
Itās online, open-source, and ready for anyone to use.
š Check it out: FreeDevTools
ā Star it on GitHub: freedevtools