Adding pagination into Next.js blog

I recently redid my blog with Next.js. I used the amazing Next.js tutorial and I was very happy with it. But as time went on and I wrote more and more articles it became apparent that I need to add paging. I am not an expert on Next and it turns out th…


This content originally appeared on DEV Community and was authored by Pavel Polívka

I recently redid my blog with Next.js. I used the amazing Next.js tutorial and I was very happy with it. But as time went on and I wrote more and more articles it became apparent that I need to add paging. I am not an expert on Next and it turns out that adding paging will not be that easy. I used static generation for my listing page and generating all the pages is not an option. I decided to switch to server-side rendering for SEO reasons but also I wanted to switch pages on the fly.

Adding API

First thing I needed to add an API call that would provide paging info and list posts.
I created a posts directory in a root api folder and in there I created a [page].js file. This file will be my api handler.

// api/posts/[page].js

import {getSortedPostsData} from "../../lib/posts";


export default function (req, res) {
    const { page } = req.query
    const allPostsData = getSortedPostsData()
    const perPage = 9
    const totalPosts = allPostsData.length
    const totalPages = totalPosts / perPage
    const start = (page - 1) * perPage
    let end = start + perPage
    if (end > totalPosts) {
        end = totalPosts
    }

    res.status(200).json({
        currentPage: page,
        perPage: perPage,
        totalCount: totalPosts,
        pageCount: totalPages,
        start: start,
        end: end,
        posts: allPostsData.slice(start, end)
    })
}

This is pretty straightforward code. It's doing some stats from an array of all posts.
Side note here, if you are deploying to Vercel, your api calls are deployed as serverless functions and you need to tell Vercel to add your markdown files to the serverless deploy. This is done via root vercel.json file.

{
  "functions": {
    "api/posts/[page].js": {
      "includeFiles": "posts/**"
    }
  }
}

The root posts directory is the place where I have all the markdown files.

Modifying blog listing page

I used the blog listing page pretty much out of the next.js tutorial. I was using static page generation. So the first thing I have done was to change it to server-side rendering.

Blog.getInitialProps = async ({ query }) => {
    const page = query.page || 1; //if page empty we request the first page
    const response = await fetch(`${server}/api/posts/${page}`)
    const posts = await response.json()
    return {
        totalCount: posts.totalCount,
        pageCount: posts.pageCount,
        currentPage: posts.currentPage,
        perPage: posts.perPage,
        posts: posts.posts,
    }
}

It fetches our new api call and returns it as our component properties.
The server variable is different for localhost and for prod. We need to specify the full path as this will be called from the server.

const dev = process.env.NODE_ENV !== 'production';
export const server = dev ? 'http://localhost:3000' : 'https://ppolivka.com';

I am using next/router to navigate between pages. And to make all the things more user-friendly I added a loading animation on route changes.

const [isLoading, setLoading] = useState(false);
const startLoading = () => setLoading(true);
const stopLoading = () => setLoading(false);

useEffect(() => {
    Router.events.on('routeChangeStart', startLoading);
    Router.events.on('routeChangeComplete', stopLoading);

    return () => {
        Router.events.off('routeChangeStart', startLoading);
        Router.events.off('routeChangeComplete', stopLoading);
    }
}, [])

To render the posts or the loading I have a if in this style.

let content;
if (isLoading) {
    content = (
        <div className={styles.loadWrapper}>
            <Spinner animation="border" role="status">
                <span className="visually-hidden">Loading...</span>
            </Spinner>
        </div>
    )
} else {
    //Generating posts list
    content = (
        <>
            {props.posts.map(({ id, date, title, image, description }) => (
                <Card className={styles.item}>
                    <Card.Img variant="top" src={image} width={360} height={215} />
                    <Card.Body>
                        <Card.Title>
                            <Link href={`/posts/${id}`}>
                                <a>
                                    {title}
                                </a>
                            </Link>
                        </Card.Title>
                        <Card.Subtitle className="mb-2 text-muted"><Date dateString={date} /></Card.Subtitle>
                        <Card.Text>
                            {description}
                        </Card.Text>
                    </Card.Body>
                </Card>
            ))}
        </>
    );
}

For the actual pagination navigation I used awesome component react-paginate.

<ReactPaginate
    previousLabel={'<'}
    nextLabel={'>'}
    breakLabel={'...'}
    breakClassName={'break-me'}
    activeClassName={'active'}
    containerClassName={'pagination'}
    subContainerClassName={'pages pagination'}
    initialPage={props.currentPage - 1}
    pageCount={props.pageCount}
    marginPagesDisplayed={2}
    pageRangeDisplayed={5}
    onPageChange={paginationHandler}
/>

It's referring the pagination handler function, that has the actual navigation logic.

const paginationHandler = (page) => {
    const currentPath = props.router.pathname;
    const currentQuery = props.router.query;
    currentQuery.page = page.selected + 1;

    props.router.push({
        pathname: currentPath,
        query: currentQuery,
    })

}

You can see the whole blog page in this Gist.

If you like this article you can follow me on Twitter.


This content originally appeared on DEV Community and was authored by Pavel Polívka


Print Share Comment Cite Upload Translate Updates
APA

Pavel Polívka | Sciencx (2021-11-11T20:14:57+00:00) Adding pagination into Next.js blog. Retrieved from https://www.scien.cx/2021/11/11/adding-pagination-into-next-js-blog/

MLA
" » Adding pagination into Next.js blog." Pavel Polívka | Sciencx - Thursday November 11, 2021, https://www.scien.cx/2021/11/11/adding-pagination-into-next-js-blog/
HARVARD
Pavel Polívka | Sciencx Thursday November 11, 2021 » Adding pagination into Next.js blog., viewed ,<https://www.scien.cx/2021/11/11/adding-pagination-into-next-js-blog/>
VANCOUVER
Pavel Polívka | Sciencx - » Adding pagination into Next.js blog. [Internet]. [Accessed ]. Available from: https://www.scien.cx/2021/11/11/adding-pagination-into-next-js-blog/
CHICAGO
" » Adding pagination into Next.js blog." Pavel Polívka | Sciencx - Accessed . https://www.scien.cx/2021/11/11/adding-pagination-into-next-js-blog/
IEEE
" » Adding pagination into Next.js blog." Pavel Polívka | Sciencx [Online]. Available: https://www.scien.cx/2021/11/11/adding-pagination-into-next-js-blog/. [Accessed: ]
rf:citation
» Adding pagination into Next.js blog | Pavel Polívka | Sciencx | https://www.scien.cx/2021/11/11/adding-pagination-into-next-js-blog/ |

Please log in to upload a file.




There are no updates yet.
Click the Upload button above to add an update.

You must be logged in to translate posts. Please log in or register.