Max Gfeller All Articles
October 20th, 2020

A Pagination Library for Vue 3

Development
Web
Vue
Pagination

This week, we at cyon published vue-use-pagination, a pagination library made for Vue 3. I wanna take some time to describe what the library does, how it can be used and how Vue 3 helped us implement it.

Why?

Integrating paginated resources in a Vue application is not that hard, but still requires a lot of custom logic that has to be implemented. Especially when the pagination itself should be a bit more dynamic: think different categories, the possibility to change the page size, not fetching the same data multiple times, and much more. Especially when you have a lot of paginated resources that have to be integrated into your application this quickly can become repetitive and cumbersome.

Because the application that we are building is one of those with a lot of resources to be integrated, we created a library called vuex-pagination which helped with those issues. It tightly integrates with a Vuex store and separates the view logic from the data fetching logic. It worked really well for us so far.

It is a very reactive approach to pagination (about which i also presented at FrontendCon 2019 in Warsaw), which makes changing the current page as easy as setting the page number and everything else happens magically in the background. Achieving this with Vue 2 was not that simple and required many "hacks" in the background. That's why the library itself does not work with Vue 3 at all. However, the new Composition API makes building reactive APIs so much easier. That's why we created a whole new library especially for Vue 3 - which in the process ended up easier to use, smaller and more flexible and stable.

How to use

There are two ways how the library can be used and depending where in your application you want your logic to reside, you can choose one over the other.

The first one is straight-forward and makes use of the way composables can be created with Vue's new Composition API. When your endpoint is /users you might add a file called useUsers.js in your composables or logic folder. This could then look like the following:

import { usePagination } from 'vue-use-pagination'

export default function useUsers ({
  page = 1,
  pageSize = 10
}) {
  const fetch = async (opts) => {
    const result = await window.fetch(`/api/users?`
      + `page=${opts.page}&pageSize=${opts.pageSize}`)
    const data = await result.json()

    return {
      total: data.total,
      items: data.data
    }
  }

  return usePagination(fetch, { page, pageSize })
}

You can now use this composable in all the components you want. The usage would look like that:

<template>
  <ul>
    <li
      v-for="user in users.items"
      :key="user.id"
    >{{ user.firstName }} {{ user.lastName }}</li>
  </ul>
  <a v-if="users.page > 1" @click="users.page--">
    Previous page
  </a>
  <a v-if="users.page < users.totalPages">
    Next page
  </a>
</template>
<script>
import useUsers from '../composables/useUsers'

export default {
  setup () {
    const users = useUsers()

    return {
      users
    }
  }
}
</script>

Now, in vuex-pagination we had a different approach to separation which we tried to implement for vue-use-pagination, too. This approach uses pre-defined resources. Those can be defined basically anywhere outside your Vue components but if you are using a Vuex store then you might want to have all the logic in the same place.

For the sake of simplicity however, i'll show you how you can define resources in your main.js file:

import { createResource } from 'vue-use-pagination'

createResource('users', async (opts) => {
  const result = await window.fetch(`/api/users?`
    + `page=${opts.page}&pageSize=${opts.pageSize}`)
  const data = await result.json()

  return {
    total: data.total,
    items: data.data
  }
})

Now these resources can be used in your components like this:

<template>
  <ul>
    <li
      v-for="user in users.items"
      :key="user.id"
    >{{ user.firstName }} {{ user.lastName }}</li>
  </ul>
  <a v-if="users.page > 1" @click="users.page--">
    Previous page
  </a>
  <a v-if="users.page < users.totalPages">
    Next page
  </a>
</template>
<script>
import { usePagination } from 'vue-use-pagination'

export default {
  setup () {
    const users = usePagination('users')

    return {
      users
    }
  }
}
</script>

When you try this out yourself you'll see that already fetched resources are read from cache. You can also try to change the pageSize and see what happens.

You can find a more detailed description, additional features like the passing of arguments (e.g. for categories) as well as a few examples in the Github repository.

Feedback

Do you think, this library could be useful to you or someone you know? Then please leave us a star on the Github repository. Thank you!

✌🏻 Did you like this article? Follow me on Twitter for more content!