© Joe Attardi 2020
J. AttardiUsing Gatsby and Netlify CMShttps://doi.org/10.1007/978-1-4842-6297-9_8

8. Blog Pagination

Joe Attardi1 
(1)
Billerica, MA, USA
 

Currently, we are displaying all of the blog posts on the index page. This is fine for just a handful of entries, but in most real-world sites, entries are broken up into pages. In this chapter, we will add some more blog entries and implement pagination.

How pagination works

We generate the list of blog posts by performing a GraphQL query. The query in its current form is shown in Listing 8-1.
{
  allMarkdownRemark(sort: { fields: frontmatter___date, order: DESC }) {
    edges {
      node {
        id
        frontmatter {
          title
          date(formatString: "MMMM D, YYYY")
        }
        fields {
          slug
        }
        excerpt
      }
    }
  }
}
Listing 8-1

The current GraphQL query

The allMarkdownRemark field accepts several arguments. Currently, we’re using the sort argument to define the sort order. There are several other arguments available as well. The two that are relevant to pagination are skip and limit. These define how many results to skip in the query results and how many results to return, respectively.

In order to do pagination in this way, we’ll need to do some refactoring of the site. Currently, we are querying for blog entries in a static query in the BlogList component. In order to use the skip and limit arguments, we’ll need to provide variables to the query. However, static queries can’t use variables - only page queries can. This means we need to use a page component for the blog list.

We’ll create a new blog page template that will list a given page of blog posts. Then, using Gatsby’s createPages API, we’ll query for the total number of blog posts, divide that into pages, then dynamically create a blog/<page number> page for each page of posts. For each page we create, we’ll pass some context parameters that will be used as variables in the page’s GraphQL query, filling in values for the skip and limit arguments.

Finally, we’ll update the index page so that it shows the first three posts, with a link to the full paginated list at the bottom.

Creating some new blog entries

Before we can use pagination, we need enough blog posts to necessitate pagination. Log in to the CMS at https://<your-site-name>.netlify.app/admin, and add some new blog posts. If you aren’t feeling particularly creative, you can go to https://lipsum.com to generate some “Lorem Ipsum” placeholder text. We will use a page size of 5, and we want to have more than one page of posts, so go ahead and create six to ten blog posts.

Hopefully that wasn’t too tedious. Remember that each new blog post creates a new commit in the Git repository, so pull the latest code from GitHub before proceeding.

Dynamically creating the blog list pages

The first page of the blog list will be at the path /blog. Subsequent pages will have a page number added to them: /blog/2, /blog/3, etc. Open the file gatsby-node.js, and add the code shown in Listing 8-2. This code is based on code from the Gatsby documentation, which can be found at www.gatsbyjs.org/docs/adding-pagination/.
const path = require('path');
const { createFilePath } = require('gatsby-source-filesystem');
exports.onCreateNode = function({ node, getNode, actions }) {
  const { createNodeField } = actions;
  if (node.internal.type === 'MarkdownRemark') {
    const slug = createFilePath({ node, getNode });
    createNodeField({
      node,
      name: 'slug',
      value: slug
    });
  }
};
exports.createPages = async function({ graphql, actions }) {
  const { createPage } = actions;
  const result = await graphql(`
    query {
      allMarkdownRemark {
        edges {
          node {
            fields {
              slug
            }
          }
        }
      }
    }
  `);
  result.data.allMarkdownRemark.edges
    .forEach(({ node }) => {
      createPage({
        path: node.fields.slug,
        component: path
          .resolve('./src/templates/blog.js'),
        context: {
          slug: node.fields.slug
        }
      });
    });
    const posts = result.data.allMarkdownRemark.edges;
    const pageSize = 5;
    const pageCount = Math.ceil(posts.length / pageSize);
    const templatePath = path.resolve('src/templates/blog-list.js');
    for (let i = 0; i < pageCount; i++) {
      let path = '/blog';
      if (i > 0) {
        path += `/${i + 1}`;
      }
      createPage({
        path,
        component: templatePath,
        context: {
          limit: pageSize,
          skip: i * pageSize,
          pageCount,
          currentPage: i + 1
        }
      });
    }
};
Listing 8-2

The updated gatsby-node.js file

First, we calculate the number of pages based on the page size and total number of posts. We then loop through each page number, dynamically creating a page for each, using the blog-list.js template page.

When creating each page, we pass some context data to it:
  • limit: The value to use for the limit argument. This is just the number of posts per page, so it will be the same for each page.

  • skip: The value to use for the skip argument. This will ensure that the query skips the posts from all previous pages and starts at the right one.

  • pageCount: The total number of pages. This is passed to each page so the page can determine if it is the last page or not, as we don’t want to show a “next page” link on the last page.

  • currentPage: The current page. This is used to determine if a page is the first page, where it shouldn’t show a “previous page” link, or the last page, where it shouldn’t show a “next page” link.

Creating the blog list template page

Now we’ll create the template page. Create a new file src/templates/blog-list.js and add the code from Listing 8-3. This code is adapted from “Pagination in GatsbyJS” by Nicky Meuleman. The original code can be found at https://nickymeuleman.netlify.app/blog/gatsby-pagination.
import React from 'react';
import { graphql, Link } from 'gatsby';
import BlogPost from '../components/BlogPost';
import Layout from '../components/Layout';
import styles from './blog-list.module.css';
export default function BlogListTemplate({ data, pageContext }) {
  // Generate the previous and next page URLs.
  const previousPage = pageContext.currentPage === 2 ?
    '/blog' :
    `/blog/${pageContext.currentPage - 1}`;
  const nextPage = `/blog/${pageContext.currentPage + 1}`;
  return (
    <Layout>
      <div id={styles.hero}>
        <h1>The Coffee Blog</h1>
      </div>
      <main className={styles.blogList}>
        {data.allMarkdownRemark.edges.map(node => (
          <BlogPost
            key={node.node.id}
            slug={node.node.fields.slug}
            title={node.node.frontmatter.title}
            date={node.node.frontmatter.date}
            excerpt={node.node.excerpt} />
        ))}
      </main>
      <div id={styles.pageLinks}>
        {pageContext.currentPage > 1 && (
          <Link to={previousPage}>
            << Previous Page
          </Link>
        )}
        {pageContext.currentPage < pageContext.pageCount && (
          <Link to={nextPage}>
            Next Page >>
          </Link>
        )}
      </div>
    </Layout>
  )
}
// The page query.
export const query = graphql`
  query BlogListQuery($skip: Int!, $limit: Int!) {
    allMarkdownRemark(
      sort: { fields: [frontmatter___date], order: DESC }
      filter: { frontmatter: { contentKey: { eq: "blog" }}}
      limit: $limit
      skip: $skip
    ) {
      edges {
        node {
          id
          frontmatter {
            title
            date(formatString: "MMMM D, YYYY")
          }
          fields {
            slug
          }
          excerpt
        }
      }
    }
  }
`;
Listing 8-3

The blog list template page

The BlogListTemplate component takes two props:
  • data: The result of the GraphQL query

  • pageContext: The page context data we passed to the createPage function

First, we generate the URLs to the previous and next pages. We then iterate over the query results and render a BlogPost component for each blog post in the results, passing in all the required props.

After rendering the list of posts, we render the previous page (if we aren’t on the first page) and next page (if we aren’t on the last page) links.

Lastly, we add the page query. This query is very similar to the query we used in the BlogList component. The main difference is that we specify limit and skip arguments for pagination. These will be substituted for the values of limit and skip from the page context before running the query.

Let’s also create the CSS module for the blog list template page. Create a new file src/templates/blog-list.module.css, as shown in Listing 8-4.
#hero {
  background: url('/latte.jpg');
  background-size: cover;
  height: 25rem;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
}
#hero h1 {
  margin: 0;
  text-transform: uppercase;
  font-size: 5rem;
  padding: 0.5rem;
  border-radius: 5px;
  background: rgba(255, 255, 255, 0.5);
}
.blog-list {
  padding: 1rem;
}
#page-links {
  padding: 1rem;
}
#page-links a {
  margin: 1rem;
}
Listing 8-4

The CSS module for the blog list

Now, let’s restart the local server by running gatsby develop. Go to the URL http://localhost:8000/blog. You should see the first five blog posts with a “Next Page” link at the bottom of the page as seen in Figure 8-1.
../images/502348_1_En_8_Chapter/502348_1_En_8_Fig1_HTML.jpg

Figure 8-1. The first page of blog posts

Clicking the “Next Page” link should go to the next page of blog posts, which will have a “Previous Page” link.

Adding a link to the new blog list page

Let’s add a link in the site header to the new blog list page. Because it is a link to another page on the site, we will use the Gatsby Link component. Open the file src/components/Layout.js and update it with the code in Listing 8-5.
import React from 'react';
import { Link } from 'gatsby';
import styles from './Layout.module.css';
export default function Layout({ children }) {
  return (
    <div>
      <header id={styles.header}>
        <div id={styles.inner}>
          <h1><Link to="/">Joe's Coffee Shop</Link></h1>
          <Link to="/blog">Blog</Link>
        </div>
      </header>
      <main id={styles.main}>
        {children}
      </main>
    </div>
  );
}
Listing 8-5

Adding a link to the header

Next, open the CSS module src/components/Layout.module.css and update it with the code in Listing 8-6.
#header {
  font-family: 'Oswald', sans-serif;
  background: url('/coffee.jpg');
  background-size: cover;
  color: #FFFFFF;
}
#header #inner {
  background: rgba(119, 79, 56, 0.85);
  padding: 1rem;
  display: flex;
  align-items: center;
}
#header h1 {
  margin: 0;
  flex-grow: 1;
}
#header h1 a {
  color: #FFFFFF;
  text-decoration: none;
}
#header a {
  color: #FFFFFF;
  text-decoration: none;
}
Listing 8-6

The updated CSS module

Refresh the page, and now there should be a link in the site header that goes to the blog list page, as shown in Figure 8-2.
../images/502348_1_En_8_Chapter/502348_1_En_8_Fig2_HTML.jpg
Figure 8-2

The blog link in the header

Updating the index page

Currently, the index page shows all the blog posts. This will make the index page very long over time as more blog posts are added (and defeats the purpose of the separate blog list page).

We can update the GraphQL query in the BlogList component used on the index page to just show the first three posts, then have a link to the full blog list at the end.

Open the file src/components/BlogList.js, and make the changes shown in Listing 8-7.
import React from 'react';
import { Link, graphql, useStaticQuery } from 'gatsby';
import BlogPost from './BlogPost';
export default function BlogList() {
  const data = useStaticQuery(graphql`
    {
      allMarkdownRemark(
        sort: { fields: frontmatter___date, order: DESC }
        limit: 3
        ) {
        edges {
          node {
            id
            frontmatter {
              title
              date(formatString: "MMMM D, YYYY")
            }
            fields {
              slug
            }
            excerpt
          }
        }
      }
    }
  `);
  return (
    <div>
      {data.allMarkdownRemark.edges.map(edge => (
        <BlogPost
          key={edge.node.id}
          slug={edge.node.fields.slug}
          title={edge.node.frontmatter.title}
          date={edge.node.frontmatter.date}
          excerpt={edge.node.excerpt}
        />
      ))}
      <div>
        <Link to="/blog">More >></Link>
      </div>
    </div>
  );
}
Listing 8-7

Limiting the number of blog posts in the BlogList component

If we refresh the site now, the index page should only be showing three blog posts, followed by a link to the full blog list page.
../images/502348_1_En_8_Chapter/502348_1_En_8_Fig3_HTML.jpg
Figure 8-3

Only three blog posts on the index page

Now that we have added blog post pagination, let’s create a new commit and push our code:
git add .
git commit -m "Add blog pagination"
git push origin master

Summary

In this chapter, we
  • Learned about the skip and limit arguments to a GraphQL query, which are used for pagination

  • Dynamically created blog list pages so they aren’t all shown on a single page

  • Used context data passed to the template page to conditionally render previous page and next page links

  • Added a link to the new blog list page from the index page

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.191.223.208