Building a blog with Gridsome: Listing All Posts and Pagination

Alex Jover Morales

In the previous post I described how to get started with Gridsome, a Vue.js technology that you can use to build data-driven static websites based on different resources that are accessible using GraphQL.

In the previous post, you can also see how to use local markdown files as a data source and render posts for each of them.

However, we don’t have a page to render a list of posts yet, so let’s see how can we manage to get that working.

Listing all Blog Posts

We already have the post template ready (thus all the posts created will use that template), but we might need a page where we list all the posts.

We’ll add that list of all posts on a page, and for this example, let’s use the home page, located at pages/Index.vue.

If we check the GraphQL explorer, we’ll see there is a allPost query available with many parameters:

allPost(
  sortBy: String = "date"
  order: SortOrder = DESC
  perPage: Int = 25
  skip: Int = 0
  page: Int = 1
  regex: String
): PostConnection

If you explore the PostConnection schema, you’ll see that we can get all posts titles and paths by using the following query, which we should put in pages/Index.vue:

pages/Index.vue

<page-query>
query Posts {
  posts: allPost {
    edges {
      node {
        title
        path
      }
    }
  }
}
</page-query>

The only thing left to do is to loop through the posts to print the links to them. For that, Gridsome already includes a <g-link> component, which is a wrapper for router-link:

pages/Index.vue

<template>
  <Layout>
    <p v-for="post in $page.posts.edges">
      <g-link :to="post.node.path">
        {{ post.node.title }}
      </g-link>
    </p>
  </Layout>
</template>

That should do it. The home page should list all your blog posts!

Adding Pagination

Listing all the posts on the same page is a bit optimistic. When we have several of them, the UX can be negatively impacted if the user has to navigate through tons of items.

That’s why we usually need pagination to provide a better navigation experience for users.

Fortunately, Gridsome is ahead and provides us with an easy way to add pagination. If you remember from the last article, the PostConnection schema from the GraphQL layer already provides us with some parameters to paginate the data:

allPost(
  sortBy: String = "date"
  order: SortOrder = DESC
  perPage: Int = 25
  skip: Int = 0
  page: Int = 1
  regex: String
): PostConnection

In particular, we can use the perPage, skip and page parameters in order to control the pagination behavior. Although skip might not be necessary very often, it can be used to tell how many items the query must skip.

Let’s continue by creating 3 or 4 more articles so we have enough to see the pagination working.

Then, we can use the @paginate directive, along with the parameters from the PostConnection in order to write a query for paginated posts:

query Posts ($page: Int) {
  posts: allPost (perPage: 2, page: $page) @paginate {
    edges {
      node {
        title
        path
      }
    }
  }
}

I’ve set the perPage parameter to 2 in order to have several pages with a few posts.

If you’d like, remember that you can try out this query in the GraphQL playground available at http://localhost:8080/___explore. If you do that, keep in mind to set the page query variable to any value starting from 1:

GraphQL Playground

In order to control the pagination, we also need the pagination information to be returned. If you inspect again the PostConnection schema, you’ll see that it has the properties totalCount and pageInfo. Let’s use them to return that info:

query Posts ($page: Int) {
  posts: allPost (perPage: 2, page: $page) @paginate {
    totalCount
    pageInfo {
      totalPages
      currentPage
      isFirst
      isLast
    }
    edges {
      node {
        title
        path
      }
    }
  }
}

If you run this query in the explorer, you’ll see data similar to this returned:

{
  "data": {
    "posts": {
      "totalCount": 5,
      "pageInfo": {
        "totalPages": 3,
        "currentPage": 1,
        "isFirst": true,
        "isLast": false
      },
      "edges": [
        {
          "node": {
            "title": "An awesome article 4",
            "path": "/blog/awesome-post-4"
          }
        }
      ]
    }
  }
}

Now that we have the query in place, let’s first update the <page-query> block in the home page:

pages/Index.vue

<page-query>
query Posts ($page: Int) {
  posts: allPost (perPage: 2, page: $page) @paginate {
    totalCount
    pageInfo {
      totalPages
      currentPage
      isFirst
      isLast
    }
    edges {
      node {
        title
        path
      }
    }
  }
}
</page-query>

Then, we can use the special Pager component that comes with Gridsome.

The Pager component works simply by passing the pageInfo data to its info property. So, to setup pagination, it’d be as simple as importing it and using it:

pages/Index.vue

<template>
  <Layout>
    <p v-for="post in $page.posts.edges">
      <g-link :to="post.node.path">
        {{ post.node.title }}
      </g-link>
    </p>
    <Pager :info="$page.posts.pageInfo"/>
  </Layout>
</template>

<page-query>
// ...
</page-query>

<script>
import { Pager } from "gridsome";

export default {
  components: {
    Pager
  }
};
</script>

If you navigate to http://localhost:8080 you should see the navigation component rendered and fully working.

Note that the component has no styles, so you might see the numbers quite close together. That’s intentional, so you have full control of the pagination styling.

If you want to see all the options of the Pager component, check out the docs.

Wrapping Up

If you reached this point, you already know all the basics on how to build a blog and any similar website using Gridsome, including both creating pages based on data and list them from any page, including pagination.

Sure, the Gridsome project is still at an early stage in the development, but it looks so exciting and seems to have a great future ahead since it’s following the Gatsby path!

Don’t forget to check out the code sample repo and the online demo for this article!

Stay cool 🦄

  Tweet It

🕵 Search Results

🔎 Searching...

Sponsored by #native_company# — Learn More
#native_title# #native_desc#
#native_cta#