How to Implement Infinite Scroll for Large Datasets Using the v-infinite-scroll Directive

The Element UI v-infinite-scroll directive attaches to any scrollable container and executes a callback when the scroll position reaches a configurable distance from the bottom, making it ideal for paginating large datasets without external dependencies.

The ElemeFE/element repository provides a lightweight, built-in solution for handling massive data lists through its Infinite Scroll directive. By attaching v-infinite-scroll to a container element, developers can implement efficient lazy loading with automatic throttling and distance-based triggers. This approach eliminates the need for heavy third-party libraries while providing fine-grained control over scroll behavior through native Vue.js directive hooks.

Core Architecture and Source Implementation

The directive is implemented in packages/infinite-scroll/src/main.js and registered globally through the Element UI entry point. It utilizes standard Vue directive hooks to manage event listeners and DOM calculations.

Directive Lifecycle and Initialization

When the directive is bound to an element via the inserted hook, it performs several initialization steps defined in the source. First, it retrieves the scrollable parent container using getScrollContainer(el). Then it parses directive attributes—including delay, distance, disabled, and immediate—through getScrollOptions(el, vm).

The core scroll handler handleScroll is wrapped with a throttle function based on the delay attribute (defaulting to 200ms) to prevent excessive callback invocations. The directive stores these references on the element itself via el[scope] for cleanup purposes. If immediate is enabled, a MutationObserver watches for DOM changes inside the container to trigger an initial scroll check when content is insufficient to fill the viewport.

Scroll Distance Calculation Logic

The handleScroll function in packages/infinite-scroll/src/main.js (lines 89-118) implements dual calculation strategies depending on the container relationship. When the scroll container equals the target element, it compares scrollTop + clientHeight against scrollHeight. For nested containers where the element sits inside a scrolling parent, it calculates the element's offset relative to the container while factoring in border widths.

If the remaining scroll distance is less than or equal to the configured distance threshold, the user-supplied callback executes. The directive automatically disconnects any MutationObserver when scrolling should no longer trigger additional loads.

Configuration Options for Performance Tuning

The directive exposes four key attributes that control how aggressively it triggers data fetching for large datasets.

Throttling with delay

The infinite-scroll-delay attribute accepts a millisecond value that controls the throttle interval applied to scroll event listeners. According to the implementation in main.js, the default 200ms interval prevents API flooding during rapid scroll gestures. For extremely heavy backend operations, increasing this to 300-500ms reduces server load while maintaining responsive UI feedback.

Distance Thresholds

Setting infinite-scroll-distance to a positive pixel value (e.g., 150) triggers the callback before the user reaches the absolute bottom of the container. This pre-fetching strategy ensures the next page of data arrives and renders while the user is still scrolling, eliminating perceived latency in large dataset navigation.

Immediate Execution and Disabled States

The infinite-scroll-immediate boolean triggers the callback immediately upon directive insertion, ensuring the first data page loads even when the container initially appears empty. Meanwhile, infinite-scroll-disabled accepts a boolean expression that temporarily halts scroll monitoring—critical for preventing concurrent API requests while a previous batch is still loading.

Implementation Examples

Basic Static Data Implementation

For datasets that load from local state or memory, attach the directive to any element with explicit height and overflow properties:

<template>
  <ul class="infinite-list" v-infinite-scroll="load" style="height: 300px; overflow: auto;">
    <li v-for="item in items" :key="item.id" class="infinite-list-item">
      {{ item.name }}
    </li>
  </ul>
</template>

<script>
export default {
  data () {
    return {
      items: [],
      page: 1,
    };
  },
  created () {
    this.load();
  },
  methods: {
    load () {
      setTimeout(() => {
        const newItems = Array.from({ length: 20 }, (_, i) => ({
          id: (this.page - 1) * 20 + i,
          name: `Item ${(this.page - 1) * 20 + i + 1}`
        }));
        this.items.push(...newItems);
        this.page += 1;
      }, 500);
    }
  }
};
</script>

Large Dataset with API Pagination

For production applications consuming remote APIs, combine the directive with loading states and conditional disabling:

<template>
  <div class="list-wrapper" style="height: 500px; overflow:auto;">
    <ul class="list" v-infinite-scroll="loadMore"
        :infinite-scroll-disabled="loading || noMore"
        infinite-scroll-distance="150"
        infinite-scroll-delay="300"
        infinite-scroll-immediate>
      <li v-for="post in posts" :key="post.id" class="list-item">
        {{ post.title }}
      </li>
    </ul>
    <p v-if="loading">Loading more…</p>
    <p v-else-if="noMore">No more results.</p>
  </div>
</template>

<script>
export default {
  data () {
    return {
      posts: [],
      page: 1,
      pageSize: 30,
      loading: false,
    };
  },
  computed: {
    noMore () {
      return this.posts.length && this.posts.length % this.pageSize !== 0;
    }
  },
  methods: {
    async loadMore () {
      if (this.loading) return;
      this.loading = true;
      try {
        const res = await fetch(
          `https://api.example.com/posts?page=${this.page}&size=${this.pageSize}`
        );
        const data = await res.json();
        this.posts.push(...data.items);
        this.page += 1;
      } finally {
        this.loading = false;
      }
    }
  }
};
</script>

Key Source Files and Type Definitions

Understanding the implementation details requires referencing specific files in the ElemeFE/element repository:

Summary

  • The v-infinite-scroll directive in Element UI provides native Vue.js infinite scrolling without external dependencies.
  • Source code in packages/infinite-scroll/src/main.js implements throttled scroll listeners and dual-mode distance calculations for both element and container scrolling scenarios.
  • Four key attributes control behavior: delay (throttling), distance (pre-fetch threshold), immediate (initial load), and disabled (conditional halting).
  • The directive automatically cleans up event listeners via the unbind hook, preventing memory leaks in single-page applications.
  • For large datasets, combine infinite-scroll-distance with infinite-scroll-disabled to implement efficient pagination that loads data before the user reaches the bottom while preventing concurrent requests.

Frequently Asked Questions

How does the v-infinite-scroll directive calculate when to trigger the load callback?

According to the source code in packages/infinite-scroll/src/main.js, the handleScroll function uses two calculation strategies. If the scroll container is the element itself, it compares scrollTop + clientHeight against scrollHeight. For nested containers, it measures the element's offset relative to the scrolling parent while accounting for border widths. When the remaining distance is less than or equal to the infinite-scroll-distance value, the callback executes.

What is the default throttle delay and how can I adjust it for high-traffic APIs?

The default delay is 200 milliseconds, implemented via a throttle wrapper around the scroll handler in the inserted hook. To reduce server load for high-traffic APIs, increase the infinite-scroll-delay attribute to 300-500ms. This value is passed directly to the throttle function defined in the directive's source.

How do I prevent the infinite scroll from triggering multiple simultaneous API requests?

Bind a boolean expression to the infinite-scroll-disabled attribute that evaluates to true while your request is in flight. Typically, this involves setting a loading flag to true at the start of your callback method and false upon completion. The directive checks this disabled state inside handleScroll before invoking your function, effectively debouncing at the application level.

Can I use v-infinite-scroll with TypeScript projects?

Yes. The repository includes TypeScript definitions in types/infinite-scroll.d.ts that specify the directive value must be a function. When using Vue with TypeScript, ensure your load method is properly typed as a function reference to satisfy the compiler checks defined in the Element UI type system.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →