Infinite scroll

Vue Bootstrap 5 Infinite scroll

This feature adds a scroll event listener (to the window or the component it's attached to if it has the overflow-y property set to scroll) and calls a callback method every time a user reaches an end of a page/container.

Note: Read the API tab to find all available options and advanced customization


Basic example

Scroll down the container below to add more items.

Note: Your element should be scrollable, for example, it should have overflow-y: scroll property like in the example below.

  • Angry
  • Dizzy
  • Flushed
  • Frown
  • Grimace
  • Grin
<template>
  <MDBListGroup
    class="container"
    style="max-height: 261px; overflow-y: scroll"
    v-mdb-infinite-scroll="infiniteScroll1"
  >
    <MDBListGroupItem
      v-for="(item, i) in listGroup1"
      :key="i"
      class="d-flex align-items-center"
    >
      <MDBIcon
        iconStyle="far"
        :icon="item.icon"
        size="3x"
        class="me-4"
      />
      {{ item.content }}
    </MDBListGroupItem>
  </MDBListGroup>
</template>
<script>
  import {
    mdbInfiniteScroll,
    MDBIcon,
    MDBListGroup,
    MDBListGroupItem
  } from 'mdb-vue-ui-kit';
  import { ref } from "vue";

  export default {
    directives: {
      mdbInfiniteScroll
    },
    components: {
      MDBIcon,
      MDBListGroup,
      MDBListGroupItem
    },
    setup() {
      const icons = [
        "Sad-Tear",
        "Meh-Blank",
        "Smile-Wink",
        "Tired",
        "Surprise",
        "Kiss-Beam",
        "Laugh-Squint"
      ];
      const listGroup1 = ref([
        {
          icon: "angry",
          content: "Angry"
        },
        {
          icon: "dizzy",
          content: "Dizzy"
        },
        {
          icon: "flushed",
          content: "Flushed"
        },
        {
          icon: "frown",
          content: "Frown"
        },
        {
          icon: "grimace",
          content: "Grimace"
        },
        {
          icon: "grin",
          content: "Grin"
        }
      ]);
      const itemIndex1 = ref(0);
      const infiniteScroll1 = () => {
        if (itemIndex1.value === icons.length - 1) {
          return;
        }
        listGroup1.value.push({
          icon: icons[itemIndex1.value].toLowerCase(),
          content: icons[itemIndex1.value]
        });
        itemIndex1.value++;
      };

      return {
        listGroup1,
        infiniteScroll1
      }
    }
  };
</script>
<script setup lang="ts">
  import {
    mdbInfiniteScroll as vMdbInfiniteScroll,
    MDBIcon,
    MDBListGroup,
    MDBListGroupItem
  } from 'mdb-vue-ui-kit';
  import { ref } from "vue";

  const icons = [
    "Sad-Tear",
    "Meh-Blank",
    "Smile-Wink",
    "Tired",
    "Surprise",
    "Kiss-Beam",
    "Laugh-Squint"
  ];
  const listGroup1 = ref([
    {
      icon: "angry",
      content: "Angry"
    },
    {
      icon: "dizzy",
      content: "Dizzy"
    },
    {
      icon: "flushed",
      content: "Flushed"
    },
    {
      icon: "frown",
      content: "Frown"
    },
    {
      icon: "grimace",
      content: "Grimace"
    },
    {
      icon: "grin",
      content: "Grin"
    }
  ]);
  const itemIndex1 = ref(0);
  const infiniteScroll1 = () => {
    if (itemIndex1.value === icons.length - 1) {
      return;
    }
    listGroup1.value.push({
      icon: icons[itemIndex1.value].toLowerCase(),
      content: icons[itemIndex1.value]
    });
    itemIndex1.value++;
  };
</script>

Direction

Use v-mdb-infinite-scroll:x to change the scrolling direction to horizontall.

Angry Dizzy Flushed Grimace Grin
<template>
  <div
    class="text-center py-3"
    style="max-width: 1500px; overflow-x: scroll; white-space: nowrap"
    v-mdb-infinite-scroll:x="infiniteScroll2"
  >
    <span v-for="(item, i) in listGroup2" :key="i" class="mx-5">
      <MDBIcon
        iconStyle="far"
        :icon="item.icon"
        size="3x"
        class="me-4"
      />
      {{ item.content }}
    </span>
  </div>
</template>
<script>
  import { mdbInfiniteScroll, MDBIcon } from 'mdb-vue-ui-kit';
  import { ref } from "vue";

  export default {
    directives: {
      mdbInfiniteScroll
    },
    components: {
      MDBIcon
    },
    setup() {
      const icons = [
        "Sad-Tear",
        "Meh-Blank",
        "Smile-Wink",
        "Tired",
        "Surprise",
        "Kiss-Beam",
        "Laugh-Squint"
      ];
      const listGroup2 = ref([
        {
          icon: "angry",
          content: "Angry"
        },
        {
          icon: "dizzy",
          content: "Dizzy"
        },
        {
          icon: "flushed",
          content: "Flushed"
        },
        {
          icon: "frown",
          content: "Frown"
        },
        {
          icon: "grimace",
          content: "Grimace"
        },
        {
          icon: "grin",
          content: "Grin"
        }
      ]);
      const itemIndex2 = ref(0);
      const infiniteScroll2 = () => {
        if (itemIndex2.value === icons.length - 1) {
          return;
        }
        listGroup2.value.push({
          icon: icons[itemIndex2.value].toLowerCase(),
          content: icons[itemIndex2.value]
        });
        itemIndex2.value++;
      };

      return {
        listGroup2,
        infiniteScroll2
      }
    }
  };
</script>
<script setup lang="ts">
  import { mdbInfiniteScroll as vMdbInfiniteScroll, MDBIcon } from 'mdb-vue-ui-kit';
  import { ref } from "vue";

  const icons = [
    "Sad-Tear",
    "Meh-Blank",
    "Smile-Wink",
    "Tired",
    "Surprise",
    "Kiss-Beam",
    "Laugh-Squint"
  ];
  const listGroup2 = ref([
    {
      icon: "angry",
      content: "Angry"
    },
    {
      icon: "dizzy",
      content: "Dizzy"
    },
    {
      icon: "flushed",
      content: "Flushed"
    },
    {
      icon: "frown",
      content: "Frown"
    },
    {
      icon: "grimace",
      content: "Grimace"
    },
    {
      icon: "grin",
      content: "Grin"
    }
  ]);
  const itemIndex2 = ref(0);
  const infiniteScroll2 = () => {
    if (itemIndex2.value === icons.length - 1) {
      return;
    }
    listGroup2.value.push({
      icon: icons[itemIndex2.value].toLowerCase(),
      content: icons[itemIndex2.value]
    });
    itemIndex2.value++;
  };
</script>

Spinners and asynchronous data

<template>
  <div
    class="infinite-scroll py-3 text-center"
    v-mdb-infinite-scroll="loadImages"
    style="max-height: 500px; overflow-y: scroll"
  >
    <div id="images">
      <img
        v-for="(image, i) in images"
        :key="i"
        :src="image"
        class="img-fluid mb-3"
      />
    </div>
    <MDBSpinner v-if="isLoading" class="mx-auto" />
  </div>
</template>
<script>
  import { mdbInfiniteScroll, MDBSpinner } from 'mdb-vue-ui-kit';
  import { ref } from "vue";

  export default {
    directives: {
      mdbInfiniteScroll
    },
    components: {
      MDBSpinner
    },
    setup() {
      const images = ref([
        "https://mdbootstrap.com/img/Photos/Slides/img%20(100).webp",
        "https://mdbootstrap.com/img/Photos/Slides/img%20(105).webp",
        "https://mdbootstrap.com/img/Photos/Slides/img%20(106).webp"
      ]);
      const isLoading = ref(false);
       const loadImages = () => {
          // Make spinner visible
         isLoading.value = true;
          // Fetch your API
         fetch('YOUR_API/getNextItem')
         .then(response => response.json)
         .then(imgUrl => {
           // Hide spinner after data loads
           isLoading.value = false;
          // Append an image element
           images.value.push(imgUrl);
           })
       };

      return {
        images,
        isLoading,
        loadImages
      }
    }
  };
</script>
<script setup lang="ts">
  import { mdbInfiniteScroll as vMdbInfiniteScroll, MDBSpinner } from 'mdb-vue-ui-kit';
  import { ref } from "vue";

  const images = ref([
    "https://mdbootstrap.com/img/Photos/Slides/img%20(100).webp",
    "https://mdbootstrap.com/img/Photos/Slides/img%20(105).webp",
    "https://mdbootstrap.com/img/Photos/Slides/img%20(106).webp"
  ]);
  const isLoading = ref(false);
  const loadImages = () => {
      // Make spinner visible
    isLoading.value = true;
      // Fetch your API
    fetch('YOUR_API/getNextItem')
    .then(response => response.json)
    .then(imgUrl => {
      // Hide spinner after data loads
      isLoading.value = false;
      // Append an image element
      images.value.push(imgUrl);
      })
  };
</script>

Window

You can apply the mdbInfiniteScroll directive to a window by placing v-mdb-infinite-scroll:window directive to the container of your scrolling elements.

<template>
  <main class="my-4" v-mdb-infinite-scroll:window="loadImages">
    <MDBContainer>
      <section class="text-center mb-4" id="posts">
        <MDBRow v-for="(pair, i) in imagePairs" :key="i">
          <MDBCol md="6" class="mb-4" v-for="(image, i) in pair" :key="i">
            <div
              class="bg-image hover-overlay ripple shadow-1-strong rounded mb-4"
              v-mdb-ripple="{ color: 'light' }"
            >
              <img
                v-mdb-lazy="{
                  src: image.src,
                  placeholder: 'https://place-hold.it/1321x583?text=Loading'
                }"
                class="w-100"
              />
              <a href="#!">
                <div
                  class="mask"
                  style="background-color: rgba(251, 251, 251, 0.2);"
                ></div>
              </a>
            </div>
            <h5>{{ image.title }}</h5>
            <p>
              {{ image.text }}
            </p>
            <MDBBtn tag="a" color="info" rounded href="#!">Read more</MDBBtn>
          </MDBCol>
        </MDBRow>
        <MDBRow v-if="isLoading">
          <MDBCol md="12">
            <MDBSpinner class="mx-auto"></MDBSpinner>
          </MDBCol>
        </MDBRow>
      </section>
    </MDBContainer>
  </main>
</template>
<script>
  import {
    mdbInfiniteScroll,
    mdbLazy,
    mdbRipple,
    MDBContainer,
    MDBRow,
    MDBCol,
    MDBBtn,
    MDBSpinner
  } from "mdb-vue-ui-kit";
  import { ref, nextTick } from "vue";

  export default {
    name: "infinite-scroll-window",
    components: { MDBContainer, MDBRow, MDBCol, MDBBtn, MDBSpinner },
    directives: {
      mdbInfiniteScroll,
      mdbLazy,
      mdbRipple
    },
    setup() {
      const items = [
        [
          {
            src: "https://mdbcdn.b-cdn.net/img/Photos/Others/images/31.webp",
            title: "This is an title of the article",
            text:
              "Lorem ipsum dolor sit amet consectetur adipisicing elit. Quisquam cupiditate veniam voluptatibus laudantium cum dolorem illo. Quos architecto deserunt saepe."
          },
          {
            src: "https://mdbcdn.b-cdn.net/img/Photos/Others/images/23.webp",
            title: "This is an title of the article",
            text:
              "Lorem ipsum dolor sit amet consectetur adipisicing elit. Quisquam cupiditate veniam voluptatibus laudantium cum dolorem illo. Quos architecto deserunt saepe."
          }
        ],
        [
          {
            src: "https://mdbcdn.b-cdn.net/img/Photos/Others/images/29.webp",
            title: "This is an title of the article",
            text:
              "Lorem ipsum dolor sit amet consectetur adipisicing elit. Quisquam cupiditate veniam voluptatibus laudantium cum dolorem illo. Quos architecto deserunt saepe."
          },
          {
            src: "https://mdbcdn.b-cdn.net/img/Photos/Others/images/27.webp",
            title: "This is an title of the article",
            text:
              "Lorem ipsum dolor sit amet consectetur adipisicing elit. Quisquam cupiditate veniam voluptatibus laudantium cum dolorem illo. Quos architecto deserunt saepe."
          }
        ],
        [
          {
            src: "https://mdbcdn.b-cdn.net/img/Photos/Others/images/25.webp",
            title: "This is an title of the article",
            text:
              "Lorem ipsum dolor sit amet consectetur adipisicing elit. Quisquam cupiditate veniam voluptatibus laudantium cum dolorem illo. Quos architecto deserunt saepe."
          },
          {
            src: "https://mdbcdn.b-cdn.net/img/Photos/Others/images/24.webp",
            title: "This is an title of the article",
            text:
              "Lorem ipsum dolor sit amet consectetur adipisicing elit. Quisquam cupiditate veniam voluptatibus laudantium cum dolorem illo. Quos architecto deserunt saepe."
          }
        ],
        [
          {
            src: "https://mdbcdn.b-cdn.net/img/Photos/Others/images/31.webp",
            title: "This is an title of the article",
            text:
              "Lorem ipsum dolor sit amet consectetur adipisicing elit. Quisquam cupiditate veniam voluptatibus laudantium cum dolorem illo. Quos architecto deserunt saepe."
          },
          {
            src: "https://mdbcdn.b-cdn.net/img/Photos/Others/images/32.webp",
            title: "This is an title of the article",
            text:
              "Lorem ipsum dolor sit amet consectetur adipisicing elit. Quisquam cupiditate veniam voluptatibus laudantium cum dolorem illo. Quos architecto deserunt saepe."
          }
        ]
      ];
      const imagePairs = ref([
        [
          {
            src: "https://mdbootstrap.com/img/Photos/Others/images/29.webp",
            title: "This is a title of the article",
            text:
              "Lorem ipsum dolor sit amet consectetur adipisicing elit. Quisquam cupiditate veniam voluptatibus laudantium cum dolorem illo. Quos architecto deserunt saepe."
          },
          {
            src: "https://mdbootstrap.com/img/Photos/Others/images/27.webp",
            title: "This is a title of the article",
            text:
              "Lorem ipsum dolor sit amet consectetur adipisicing elit. Quisquam cupiditate veniam voluptatibus laudantium cum dolorem illo. Quos architecto deserunt saepe."
          }
        ],
        [
          {
            src: "https://mdbootstrap.com/img/Photos/Others/images/25.webp",
            title: "This is a title of the article",
            text:
              "Lorem ipsum dolor sit amet consectetur adipisicing elit. Quisquam cupiditate veniam voluptatibus laudantium cum dolorem illo. Quos architecto deserunt saepe."
          },
          {
            src: "https://mdbootstrap.com/img/Photos/Others/images/24.webp",
            title: "This is a title of the article",
            text:
              "Lorem ipsum dolor sit amet consectetur adipisicing elit. Quisquam cupiditate veniam voluptatibus laudantium cum dolorem illo. Quos architecto deserunt saepe."
          }
        ],
        [
          {
            src: "https://mdbootstrap.com/img/Photos/Others/images/31.webp",
            title: "This is a title of the article",
            text:
              "Lorem ipsum dolor sit amet consectetur adipisicing elit. Quisquam cupiditate veniam voluptatibus laudantium cum dolorem illo. Quos architecto deserunt saepe."
          },
          {
            src: "https://mdbootstrap.com/img/Photos/Others/images/23.webp",
            title: "This is a title of the article",
            text:
              "Lorem ipsum dolor sit amet consectetur adipisicing elit. Quisquam cupiditate veniam voluptatibus laudantium cum dolorem illo. Quos architecto deserunt saepe."
          }
        ]
      ]);
      const itemsIndex = ref(0);
      const isLoading = ref(false);
      const loadImages = () => {
        if (itemsIndex.value === items.length - 1) {
          return;
        }
        isLoading.value = true;
        imagePairs.value.push(items[itemsIndex.value]);
        nextTick(() => {
          isLoading.value = false;
        });
        itemsIndex.value++;
      };

      return {
        imagePairs,
        isLoading,
        loadImages
      };
    }
  };
  </script>
<script setup lang="ts">
  import {
    mdbInfiniteScroll as vMdbInfiniteScroll,
    mdbLazy as vMdbLazy,
    mdbRipple as vMdbRipple,
    MDBContainer,
    MDBRow,
    MDBCol,
    MDBBtn,
    MDBSpinner
  } from "mdb-vue-ui-kit";
  import { ref, nextTick } from "vue";

  const items = [
    [
      {
        src: "https://mdbcdn.b-cdn.net/img/Photos/Others/images/31.webp",
        title: "This is an title of the article",
        text:
          "Lorem ipsum dolor sit amet consectetur adipisicing elit. Quisquam cupiditate veniam voluptatibus laudantium cum dolorem illo. Quos architecto deserunt saepe."
      },
      {
        src: "https://mdbcdn.b-cdn.net/img/Photos/Others/images/23.webp",
        title: "This is an title of the article",
        text:
          "Lorem ipsum dolor sit amet consectetur adipisicing elit. Quisquam cupiditate veniam voluptatibus laudantium cum dolorem illo. Quos architecto deserunt saepe."
      }
    ],
    [
      {
         src: "https://mdbcdn.b-cdn.net/img/Photos/Others/images/29.webp",
        title: "This is an title of the article",
        text:
          "Lorem ipsum dolor sit amet consectetur adipisicing elit. Quisquam cupiditate veniam voluptatibus laudantium cum dolorem illo. Quos architecto deserunt saepe."
      },
      {
        src: "https://mdbcdn.b-cdn.net/img/Photos/Others/images/27.webp",
        title: "This is an title of the article",
        text:
          "Lorem ipsum dolor sit amet consectetur adipisicing elit. Quisquam cupiditate veniam voluptatibus laudantium cum dolorem illo. Quos architecto deserunt saepe."
      }
    ],
    [
      {
        src: "https://mdbcdn.b-cdn.net/img/Photos/Others/images/25.webp",
        title: "This is an title of the article",
        text:
          "Lorem ipsum dolor sit amet consectetur adipisicing elit. Quisquam cupiditate veniam voluptatibus laudantium cum dolorem illo. Quos architecto deserunt saepe."
      },
      {
        src: "https://mdbcdn.b-cdn.net/img/Photos/Others/images/24.webp",
        title: "This is an title of the article",
        text:
          "Lorem ipsum dolor sit amet consectetur adipisicing elit. Quisquam cupiditate veniam voluptatibus laudantium cum dolorem illo. Quos architecto deserunt saepe."
      }
    ],
    [
      {
        src: "https://mdbcdn.b-cdn.net/img/Photos/Others/images/31.webp",
        title: "This is an title of the article",
        text:
          "Lorem ipsum dolor sit amet consectetur adipisicing elit. Quisquam cupiditate veniam voluptatibus laudantium cum dolorem illo. Quos architecto deserunt saepe."
      },
      {
        src: "https://mdbcdn.b-cdn.net/img/Photos/Others/images/32.webp",
        title: "This is an title of the article",
        text:
          "Lorem ipsum dolor sit amet consectetur adipisicing elit. Quisquam cupiditate veniam voluptatibus laudantium cum dolorem illo. Quos architecto deserunt saepe."
      }
    ]
  ];
  const imagePairs = ref([
    [
      {
        src: "https://mdbootstrap.com/img/Photos/Others/images/29.webp",
        title: "This is a title of the article",
        text:
          "Lorem ipsum dolor sit amet consectetur adipisicing elit. Quisquam cupiditate veniam voluptatibus laudantium cum dolorem illo. Quos architecto deserunt saepe."
      },
      {
        src: "https://mdbootstrap.com/img/Photos/Others/images/27.webp",
        title: "This is a title of the article",
        text:
          "Lorem ipsum dolor sit amet consectetur adipisicing elit. Quisquam cupiditate veniam voluptatibus laudantium cum dolorem illo. Quos architecto deserunt saepe."
      }
    ],
    [
      {
        src: "https://mdbootstrap.com/img/Photos/Others/images/25.webp",
        title: "This is a title of the article",
        text:
          "Lorem ipsum dolor sit amet consectetur adipisicing elit. Quisquam cupiditate veniam voluptatibus laudantium cum dolorem illo. Quos architecto deserunt saepe."
      },
      {
        src: "https://mdbootstrap.com/img/Photos/Others/images/24.webp",
        title: "This is a title of the article",
        text:
          "Lorem ipsum dolor sit amet consectetur adipisicing elit. Quisquam cupiditate veniam voluptatibus laudantium cum dolorem illo. Quos architecto deserunt saepe."
      }
    ],
    [
      {
        src: "https://mdbootstrap.com/img/Photos/Others/images/31.webp",
        title: "This is a title of the article",
        text:
          "Lorem ipsum dolor sit amet consectetur adipisicing elit. Quisquam cupiditate veniam voluptatibus laudantium cum dolorem illo. Quos architecto deserunt saepe."
      },
      {
        src: "https://mdbootstrap.com/img/Photos/Others/images/23.webp",
        title: "This is a title of the article",
        text:
          "Lorem ipsum dolor sit amet consectetur adipisicing elit. Quisquam cupiditate veniam voluptatibus laudantium cum dolorem illo. Quos architecto deserunt saepe."
      }
    ]
  ]);
  const itemsIndex = ref(0);
  const isLoading = ref(false);
  const loadImages = () => {
    if (itemsIndex.value === items.length - 1) {
      return;
    }
    isLoading.value = true;
    imagePairs.value.push(items[itemsIndex.value]);
    nextTick(() => {
      isLoading.value = false;
    });
    itemsIndex.value++;
  };
</script>

Infinite scroll - API


Import

<script>
  import {
    mdbInfiniteScroll
  } from 'mdb-vue-ui-kit';
</script>

Arguments

Argument Description
x Set horizontal scroll direction
window Set scroll parent on window element