Building an infinite scroll in Vue.js (Vuex + API)

in Code Write a comment

In this tutorial, you’ll learn how to build an infinite scroll without using any 3-rd party packages. Ready?

Let’s break our goal down:
1. We need to detect if the user has scrolled to the bottom of the page. I’ve already covered this case in another article.
2. Load/fetch more items if the user has reached the bottom of the page.

Nothing complicated, I wouldn’t recommend installing a custom npm package.

Vue.js infinite scroll full example

new Vue({
  el: "#app",
  data: {
    scrolledToBottom: true, // make sure it's set to true
    isLoadingMore: null,
    moreItems: []
  },
  methods: {
    scroll () {
      window.onscroll = () => {
        let bottomOfWindow = Math.max(window.pageYOffset, document.documentElement.scrollTop, document.body.scrollTop) + window.innerHeight === document.documentElement.offsetHeight

        if (bottomOfWindow) {
          if (this.itemsPagination.next && this.scrolledToBottom) {
            this.scrolledToBottom = false
            this.isLoadingMore = true
            this.$store.dispatch('LOAD_ITEMS_PAGINATION', { pagination: this.itemsPagination.next }).then((response) => {
              setTimeout(() => {
                if (this.$store.getters.getItemsPagination.results) {
                  this.moreItems.push(...this.$store.getters.getItemsPagination.results)
                }
                this.isLoadingMore = false
                this.scrolledToBottom = true
              }, 300)
            }, error => {
              console.log(error)
              this.isLoadingMore = false
            })
          }
        }
      }
    }
  },
  mounted () {
    this.scroll()
  },
  computed: {
    ...mapGetters({
      items: 'getItems',
      itemsPagination: 'getItemsPagination'
    })
  },
  created () {
    this.$store.dispatch('LOAD_ITEMS') // replace it with your action
  }
})

This example works like this when the component is loaded we immediately dispatch a Vuex action LOAD_ITEMS (you should replace it with your own action).

We then access a list of items using a computed property items and render it in the HTML template, however you like.


Hi, I'm Renat 👋

 


If you’re using a back-end server for your API calls, e.g. Django REST Framework make sure Pagination setting is on. As we’ll be using it to fetch more items.

Call a scroll() method in the mounted() function:

mounted () {
  this.scroll()
}

Now every time the user reaches the bottom of the page the scroll() method will load more items if they exist (by dispatching a LOAD_ITEMS_PAGINATION action and pushing the results to the moreItems array).

It won’t dispatch LOAD_ITEMS_PAGINATION action if there are no items to load.

HTML part

In the HTML part of your component, you can first render a list of items using v-for loop and then below it check using v-if if there are items in the moreItems array and render them again using the v-for loop.

Store.js (NEW!)

// getters.js

const getters = {
  getItems (state) {
    return state.items
  },
  getItemsPagination: (state) => {
    return state.itemsPagination
  }
}

// actions.js

LOAD_ITEMS ({ commit }) {
  return new Promise((resolve, reject) => {
    axios.get(process.env.VUE_APP_BASE_URL + 'api/items/')
      .then(response => {
      resolve(response)
      commit('SET_LOADED_ITEMS', response.data.results)
      commit('SET_LOADED_ITEMS_PAGINATION', response.data)
    })
      .catch(error => {
      reject(error)
      console.log(error)
    })
  })
},

LOAD_ITEMS_PAGINATION ({ commit }, { pagination }) {
  return new Promise((resolve, reject) => {
    axios.get(pagination)
      .then(response => {
      resolve(response)
      commit('SET_LOADED_ITEMS_PAGINATION', response.data)
    })
      .catch(error => {
      reject(error)
      console.log(error)
    })
  })
}

// index.js -> mutations part

SET_LOADED_ITEMS (state, payload) {
  state.items = payload
},

SET_LOADED_ITEMS_PAGINATION (state, payload) {
  state.itemsPagination = payload
},

Easy peasy.

If you find this post useful, or if you have any questions, please let me know in the comments below. 
Cheers,
Renat Galyamov

Want to share this with your friends?
👉renatello.com/vue-js-infinite-scroll

PS: Make sure you check other Vue.js tutorials, e.g. how to create global components in Vue.js.

A collection of UI components for
Bootstrap 4/5 and Vue.js

Get now

Write a Comment

Comment

4 Comments

  1. Good info, but incomplete for people following along. Need to include your store.js at the very least or link to the full working code.

    • Thanks Darius. I thought it would be enough as everyone has their own way of implementing store.js. I might add a full example later

  2. Hi , nice example and nice work ,but incomplete, we need to see what is in your state, and how you handle pagination.
    cheers

    • Hi Hanucka, it will depend on your backend (API server). In the example above I was using Django REST Framework.