Skip to content

Paginate

Bryce Russell edited this page Nov 8, 2022 · 16 revisions

This is the SSR component version of Astro's SSG paginate() method

Features:

  • Paginate an array of data
  • Compatible with indexes
  • Compatible with <Pagination> component for page navigation
  • Alternatively render a specific page using the page number as a named slot
  • New: Render an 'error' page using the error slot if a page number that doesn't exist is accessed

Example:

---
import { Paginate } from 'astro-headless-ui';

const posts = await fetch('https://jsonplaceholder.typicode.com/posts').then(response => response.json())
---

<Paginate page={Astro.params.page} data={posts}>
    {page => (
        <section>
            { page.data.map(post => (
                <article>
                    <h2>{post.title}</h2>
                    <p>{post.body}</p>
                </article>
            ))}
        </section>
    )}
</Paginate>

Props

data: any[]

Array of data to split into pages

size?: string|number

Default: 10

Number of items assigned to a page

page: string|number

Default: 1

Page number of page to display/render

Example:

// src/pages/[...page].astro
<Paginate page={Astro.params.page}>
    ...
</Paginate>

Slots

default

Default for render for page

<Paginate>
    {page => (
      ...
    )}
</Paginate>

error

This slot is rendered if the page prop is not a number or if its larger than the total number of pages Example: trying to access page 11 when there are only 10 pages

[number]

Target a specific page for an alternative render using the page number as a named slot

In this example the first page of the pagination is completely replaced with a single element <h1>First Page</h1>

<Paginate>
    <first slot="1">{() => <h1>First Page</h1>}</first>
    {page => (
      ...
    )}
</Paginate>

Slot Arguments

All slots are passed a page argument

interface Page {
    data: any[]; // Array of data for the current page
    start: number; // Index of first item on current page
    end: number; // Index of last item on current page
    size: number; // How many items per-page
    total: number; // The total number of items across all pages
    current: number; // The current page number, starting with 1
    last: number; // The total number of pages
}
<Paginate>
    {page => (
        ...
    )}
</Paginate>

Examples

Adding navigation with Pagination

Easily add page navigation to your page using the <Pagination> component

// 'src/pages/posts/[page].astro'
---
const posts = await fetch('https://jsonplaceholder.typicode.com/posts')
    .then(response => response.json())
---
<Paginate data={posts} size="10" page={Astro.params.page}>
    { page => (
        <section>
            { page.data.map(post => (
                <article>
                    <h2>{post.id} - {post.title}</h2>
                    <p>{post.body}</p>
                </article>
            ))}
        </section>
        <nav>
            <Pagination url="/posts" total={page.last} current={page.current}>
                <active slot="active">{page => <span>{page.number}</span>}</active>
                <span slot="disabled">...</span>
                {page => <a href={page.href}>{page.number}</a>}
            </Pagination>
        </nav>
    )}
</Paginate>

Alternative render for first page

Target a page for alternative render by using the page number as a named slot

// 'src/pages/posts/[page].astro'
---
const posts = await fetch('https://jsonplaceholder.typicode.com/posts')
    .then(response => response.json())
---
<Paginate data={posts} size="10" page={Astro.params.page}>
    <first slot="1">{ page => (
        <section>
            <h1>This is the first page</h1>
            { page.data.map(post => (
                <article>
                    <h2>{post.id} - {post.title}</h2>
                    <p>{post.body}</p>
                </article>
            ))}
        </section>
    )}</first>
    { page => (
        <section>
            { page.data.map(post => (
                <article>
                    <h2>{post.id} - {post.title}</h2>
                    <p>{post.body}</p>
                </article>
            ))}
        </section>
    )}
</Paginate>

Render error slot if page doesn't exist

Render an error page if trying to access a page number larger than the total number of pages

// 'src/pages/posts/[page].astro'
---
const posts = await fetch('https://jsonplaceholder.typicode.com/posts')
    .then(response => response.json())
---
<Paginate data={posts} size="10" page={Astro.params.page}>
    { page => (
        <section>
            { page.data.map(post => (
                <article>
                    <h2>{post.id} - {post.title}</h2>
                    <p>{post.body}</p>
                </article>
            ))}
        </section>
    )}
    <section slot="error">
        <p>This page does not exist</p>
    </section>
</Paginate>

Paginate with an index

To paginate with an index add the <Paginate> component to the index (index.astro) of your dynamic [page].astro route

If using the <Pagination> component for navigation you must pass the index prop

// 'src/pages/posts/index.astro' and 'src/pages/posts/[page].astro'
---
const posts = await fetch('https://jsonplaceholder.typicode.com/posts')
    .then(response => response.json())
---
<Paginate data={posts} size="10" page={Astro.params.page}>
    { page => (
        <section>
            { page.data.map(post => (
                <article>
                    <h2>{post.id} - {post.title}</h2>
                    <p>{post.body}</p>
                </article>
            ))}
        </section>
        <nav>
            <Pagination index url="/posts" total={page.last} current={page.current}>
                <active slot="active">{page => <span>{page.number}</span>}</active>
                <span slot="disabled">...</span>
                {page => <a href={page.href}>{page.number}</a>}
            </Pagination>
        </nav>
    )}
    <section slot="error">
        <p>This page does not exist</p>
    </section>
</Paginate>
Clone this wiki locally