Vue Pagination with Axios and API (Server Side pagination) example

In this tutorial, I will show you how to make Pagination in a Vue.js Application with existing API (Server Side pagination) using Axios and Bootstrap-Vue.

Related Posts:
Vue.js 2 CRUD Application with Vue Router & Axios
Vue.js JWT Authentication with Vuex and Vue Router
Vue File Upload example using Axios

Using Vuetify Pagination Component:
Vuetify Pagination (Server side) example


Vue Pagination with API overview

One of the most important things to make a website friendly is the response time, and pagination comes for this reason. For example, this bezkoder.com website has hundreds of tutorials, and we donโ€™t want to see all of them at once. Paging means displaying a small number of all, by a page.

Assume that we have tutorials table in database like this:

vue-pagination-axios-api-bootstrap-vue-db-table

Our Vue.js app will display the result with pagination:

vue-pagination-axios-api-bootstrap-vue-default-paging

Change to a page with larger index:

vue-pagination-axios-api-bootstrap-vue-page-change

We can change quantity of items per page:

vue-pagination-axios-api-bootstrap-vue-items-per-page-change

Or paging with filter:

vue-pagination-axios-api-bootstrap-vue-paging-with-filter

The API for this Vue client can be found at one of following posts:
Node.js Express Pagination with MySQL
Node.js Express Pagination with PostgreSQL
Node.js Express Pagination with MongoDB
Spring Boot Pagination & Filter example | Spring JPA, Pageable
Spring Boot MongoDB Pagination example with Spring Data

These Servers will exports API for pagination (with/without filter), here are some url samples:

  • /api/tutorials?page=1&size=5
  • /api/tutorials?size=5: using default value for page
  • /api/tutorials?page=1: using default value for size
  • /api/tutorials?title=data&page=1&size=3: pagination & filter by title containing ‘data’

This is structure of the response (server-side pagination) for the HTTP GET request:

{
    "totalItems": 8,
    "tutorials": [...],
    "totalPages": 3,
    "currentPage": 1
}

This is a kind of server-side paging, where the server sends just a single page at a time. Bootstrap-Vue Pagination component supports this scenario, so we actually only need to use tutorials and totalItems when working with this library.

Bootstrap-Vue Pagination component

Bootstrap-Vue provides Pagination component (b-pagination) that enables the user to select a specific page from a range of pages.

This is a basic Pagination component:

vue-pagination-axios-api-bootstrap-vue-default

<b-pagination
  v-model="currentPage"
  :total-rows="rows"
  :per-page="perPage"
></b-pagination>
  • v-model: bind the current page number value
  • total-rows: total items
  • per-page: number of items per page

Notice that page numbers are indexed from 1, the number of pages is computed from total-rows and per-page.

You can also customize the appearance of Pagination component with content/size of the buttons, pill style like following samples:

vue-pagination-axios-api-bootstrap-vue-customize-pagination-component

The corresponding code will be:

<b-pagination
  v-model="page"
  :total-rows="count"
  :per-page="pageSize"
  size="sm"
></b-pagination>

<b-pagination
  v-model="page"
  :total-rows="count"
  :per-page="pageSize"
  size="lg"
></b-pagination>

<b-pagination
  v-model="page"
  :total-rows="count"
  :per-page="pageSize"
  pills
></b-pagination>

<b-pagination
  v-model="page"
  :total-rows="count"
  :per-page="pageSize"
></b-pagination>

<b-pagination
  v-model="page"
  :total-rows="count"
  :per-page="pageSize"
  first-number
  last-number
></b-pagination>

<b-pagination
  v-model="page"
  :total-rows="count"
  :per-page="pageSize"
  first-text="First"
  prev-text="Prev"
  next-text="Next"
  last-text="Last"
></b-pagination>

For more details, please visit:
BootstrapVue Pagination Component Reference

For handling page changes, we use change event:

<template>
  <div>
      <b-pagination
        @change="handlePageChange"
      ></b-pagination>
      ...
    </div>
  </div>
</template>

<script>
export default {
  data() {
    return {
      page: 1,
    };
  },
  methods: {
    handlePageChange(value) {
      this.page = value;
    },
  },
};
</script>

handlePageChange will be invoked whenever the page changes via user interaction (click), value is the selected page number.

Technology

  • Vue 2.6
  • axios 0.19.2
  • bootstrap 4.5.0
  • bootstrap-vue 2.16.0

Setup Vue.js Application

Open cmd at the folder you want to save Project folder, run command:
vue create vue-js-client-crud

You will see some options, choose default (babel, eslint).
After the process is done. We create new folders and files like the following tree:


public

index.html

src

components

AddTutorial.vue

Tutorial.vue

TutorialsList.vue

services

TutorialDataService.js

App.vue

main.js

package.json


You can follow step by step, or get source code in this post:
Vue.js 2 CRUD Application with Vue Router & Axios

The Vue Project contains structure that we only need to add some changes (in TutorialsList.vue and TutorialDataService.js) to make the pagination work well.

vue-pagination-axios-api-bootstrap-vue-project-structure

Or you can get the new Github source code at the end of this tutorial.

Setup Bootstrap Vue for Vue Pagination App

We need to install both Bootstrap core and Bootstrap Vue with command:
npm install bootstrap-vue bootstrap

Register BootstrapVue in your app entry point:

src/main.js

import Vue from 'vue'
import App from './App.vue'
import { BootstrapVue } from 'bootstrap-vue'
import 'bootstrap/dist/css/bootstrap.css'
import 'bootstrap-vue/dist/bootstrap-vue.css'

...
Vue.use(BootstrapVue)

new Vue({
  render: h => h(App),
}).$mount('#app')

Initialize Axios

Now we’re gonna install axios with command: npm install axios.
Then, under src folder, we create http-common.js file like this:

import axios from "axios";

export default axios.create({
  baseURL: "http://localhost:8080/api",
  headers: {
    "Content-type": "application/json"
  }
});

Remember to change the baseURL, it depends on REST APIs url that your Server configures.

For more details about ways to use Axios, please visit:
Axios request: Get/Post/Put/Delete example

Create Data Service

In this step, we’re gonna create a service that uses axios object above to send HTTP requests.

services/TutorialDataService.js

import http from "../http-common";

class TutorialDataService {
  getAll(params) {
    return http.get("/tutorials", { params });
  }

  // other CRUD methods
}

export default new TutorialDataService();

In the code above, you can see that we pass params object to GET method.
The params object will have one, two or all fields: title, page, size.

Create Vue Component with Pagination

This component has:

  • a search bar for finding Tutorials by title.
  • a select element for quantity of items per page.
  • a Bootstrap-Vue Pagination component
  • a tutorials array displayed as a list on the left.
  • a selected Tutorial which is shown on the right.

vue-pagination-axios-api-bootstrap-vue-default-paging

components/TutorialsList.vue

<template>
  <div class="list row">
    <div class="col-md-8">
      <div class="input-group mb-3">
        <input
          type="text"
          class="form-control"
          placeholder="Search by title"
          v-model="searchTitle"
        />
        <div class="input-group-append">
          <button
            class="btn btn-outline-secondary"
            type="button"
            @click="page = 1; retrieveTutorials();"
          >
            Search
          </button>
        </div>
      </div>
    </div>

    <div class="col-md-12">
      <div class="mb-3">
        Items per Page:
        <select v-model="pageSize" @change="handlePageSizeChange($event)">
          <option v-for="size in pageSizes" :key="size" :value="size">
            {{ size }}
          </option>
        </select>
      </div>

      <b-pagination
        v-model="page"
        :total-rows="count"
        :per-page="pageSize"
        prev-text="Prev"
        next-text="Next"
        @change="handlePageChange"
      ></b-pagination>
    </div>

    <div class="col-md-6">
      <h4>Tutorials List</h4>
      <ul class="list-group" id="tutorials-list">
        <li
          class="list-group-item"
          :class="{ active: index == currentIndex }"
          v-for="(tutorial, index) in tutorials"
          :key="index"
          @click="setActiveTutorial(tutorial, index)"
        >
          {{ tutorial.title }}
        </li>
      </ul>
    </div>

    ...
  </div>
</template>

<script>
export default {
  ...
}
</script>

We will have following properties in data:
– search and display Tutorials:

  • searchTitle
  • tutorials
  • currentTutorial and currentIndex

– pagination:

  • page: current page
  • count: total pages
  • pageSize: number of items in each page

For retrieving pagination data, we’re gonna use TutorialDataService.getAll() methods.

<template>
  ...
</template>

<script>
import TutorialDataService from "../services/TutorialDataService";

export default {
  name: "tutorials-list",
  data() {
    return {
      tutorials: [],
      currentTutorial: null,
      currentIndex: -1,
      searchTitle: "",

      page: 1,
      count: 0,
      pageSize: 3,

      pageSizes: [3, 6, 9],
    };
  },
  methods: {
    getRequestParams(searchTitle, page, pageSize) {
      let params = {};

      if (searchTitle) {
        params["title"] = searchTitle;
      }

      if (page) {
        params["page"] = page - 1;
      }

      if (pageSize) {
        params["size"] = pageSize;
      }

      return params;
    },

    retrieveTutorials() {
      const params = this.getRequestParams(
        this.searchTitle,
        this.page,
        this.pageSize
      );

      TutorialDataService.getAll(params)
        .then((response) => {
          const { tutorials, totalItems } = response.data;
          this.tutorials = tutorials;
          this.count = totalItems;

          console.log(response.data);
        })
        .catch((e) => {
          console.log(e);
        });
    },

    handlePageChange(value) {
      this.page = value;
      this.retrieveTutorials();
    },

    handlePageSizeChange(event) {
      this.pageSize = event.target.value;
      this.page = 1;
      this.retrieveTutorials();
    },

    ...
  }
};
</script>

Let me explain some lines of code.

In the retrieveTutorials() method:
– We get searchTitle, page, pageSize value and transform them into params object:

{
    "title": searchTitle,
    "page": page - 1,
    "size": pageSize
}

– We use tutorials and totalItems as count value from the response data:

{
    "totalItems": 8,
    "tutorials": [...],
    "totalPages": 3,
    "currentPage": 1
}

handlePageChange() and handlePageSizeChange() methods are for setting new page and pageSize and then invokes retrieveTutorials() that updates the tutorials List after pagination information changes.

Configure Port for Vue Pagination App

Because most of HTTP Server use CORS configuration that accepts resource sharing retricted to some sites or ports, so we also need to configure port for our App.

In project root folder, create vue.config.js file with following content:

module.exports = {
  devServer: {
    port: 8081
  }
}

Run Vue Pagination App

First you need to run the Server at one of following posts:

Then you can run our App with command: npm run serve.
If the process is successful, open Browser with Url: http://localhost:8081/ and check it.

Conclusion

Today we’ve built a Vue Pagination example that use Axios to consume API (server-side pagination) successfully with Bootstrap Vue for Pagination UI. I hope you apply it in your project at ease.

If you want to use Vuetify Pagination Component, please visit:
Vuetify Pagination (Server side) example

vuetify-pagination-server-side-pagination-component

Happy learning, see you again!

Further Reading

For more details about ways to use Axios, please visit:
Axios request: Get/Post/Put/Delete example

Source Code

You can find the complete source code for this tutorial on Github.

11 thoughts to “Vue Pagination with Axios and API (Server Side pagination) example”

  1. Nice tutorial! Funny enough, what I really liked was the screenshot of various ways you might style pagination e.g. circular buttons and what not. I also like how you tied it into how you’d actually use against a server API with subsequent requests as you page ๐Ÿ‘

    I’m building a ui component library for react, vue 3, angular, and svelte and so I looked at many many tutorials, github source code, etc., etc., before starting to code mine and there are a couple of things I think are very important for a pagination component folks should consider:
    – configurability of the view
    – supporting sibling offset of both 1 or 2 (ant design and zendesk appear to use padding or offset of 1 but I’ve seen many others that use offset of 2; so I decided to just support both).
    – accessibility (aria-current, aria-disabled, etc., are crucial to support screenreaders and build an inclusive component)
    – ideally, support Vue 3’s composition api. Once I dug in a bit I found the vue 3 composition api way of doing things much more flexible and clean

    I’d invite you to look at AgnosticUI’s vue 3 pagination implementation and see what you think bezkoder — thanks for this tutorial and the visualizations too ๐Ÿ˜‰

  2. If you have time could you to make a pagination from scratch server side also ?
    This would be axios + pinia or any state management

  3. What part of this is server paginated? You get all the tutorials from the server. This is not true server-side pagination in which you retrieve only what you need for that page

    1. Hi, this is exactly Vue Client for Server-side Pagination where the server takes a number of parameters from the client (current page, page size…) and performs whatever search required to get just the relevant records.

      This Vue Client only needs to send request with page, size and searching field.

      Do you want the Client work as a server? I don’t think that it is a good idea.

Comments are closed to reduce spam. If you have any question, please send me an email.