Vue-Infinity helps you build resource-efficient UIs by only keeping visible parts of your app in memory. It also includes a an easy to style carousel that can scroll through unlimited items without burning through RAM.
Provides reactive, paginated access to large datasets with full type support.
- Paginated data access
- Caching with automatic unloading of older pages based on least-recently-used basis
- Item access by index
- Supports cancellation of in-flight network requests using
AbortController
Example:
const { pages, getItem, fetchPage } = useInfiniteList({
fetchItems: async (page, signal) => {
const response = await fetch(`/api/items?page=${page}`, { signal });
return response.json();
},
itemsPerPage: 50,
maxPagesToCache: 5
});
A general-purpose virtual scroll component optimized for grid-like or carousel-based layouts.
- Integrates directly with
InfiniteList
for managing data access - Customizable number of rows and columns
- Configurable as a horizontal or vertical scroller
- Supports custom templates for each item
- Supports custom loading templates
- Allows scrolling to any item with CSS-based scroll snapping
- Supports dynamic item sizing by providing an
onGetItemAspectRatio
callback function that returns the aspect ratio of an item.
Example:
<template>
<InfiniteCarousel
:infinite-list="infiniteList"
:height="'50vh'"
:width="'100%'"
:numColsToShow="3"
:numRowsToShow="2"
>
<template #item="{ item, index }">
<img :src="item.url" :alt="item.title" class="carousel-img"/>
</template>
</InfiniteCarousel>
</template>
<script setup>
import { useInfiniteList } from 'vue-infinity';
const infiniteList = useInfiniteList({
fetchItems: (page) => fetchPage(page),
itemsPerPage: 20,
maxPagesToCache: 5
});
</script>
Example with Dynamic Item Sizing:
<template>
<InfiniteCarousel
:infinite-list="infiniteList"
:height="'60vh'"
:width="'100%'"
:numColsToShow="4"
:numRowsToShow="3"
:onGetItemAspectRatio="getItemAspectRatio"
>
<template #item="{ item }">
<img :src="item.url" :alt="item.title" class="carousel-img"/>
</template>
</InfiniteCarousel>
</template>
<script setup>
import { useInfiniteList } from 'vue-infinity';
const fetchItems = async (page) => {
// Replace with your actual data fetching logic
const response = await fetch(`/api/items?page=${page}`);
return response.json();
};
const infiniteList = useInfiniteList({
fetchItems,
itemsPerPage: 30, // Adjust based on your data
maxPagesToCache: 5
});
const getItemAspectRatio = (item) => {
// Assuming item has width and height properties, or can be parsed from URL
if (!item || !item.url) {
return 1; // Default to square if aspect ratio cannot be determined
}
try {
const urlParts = item.url.split('/');
const width = parseInt(urlParts[urlParts.length - 2], 10);
const height = parseInt(urlParts[urlParts.length - 1].split('?')[0], 10);
return width / height;
} catch (e) {
console.error("Error parsing item aspect ratio:", e);
return 1;
}
};
</script>
Enhances the native IntersectionObserver
by automatically handling new elements and cleaning up removed ones.
- Assign it to a target container and it automatically:
- observes newly added elements
- Stops observing removed elements
- Allows filtering elements to observe using custom logic
- Cleans up automatically on component unmount
Example:
const containerRef = ref<HTMLElement>();
const { disconnect } = useAutoObserver(
containerRef,
(entries) => {
entries.forEach(entry => {
console.log('Element visibility changed:', entry.isIntersecting);
});
},
{
rootMargin: '200px',
threshold: 0.1,
filter: el => el.classList.contains('observe-me')
}
);
The Ghost
component optimizes performance by conditionally rendering its slot content. When off-screen, the content is replaced by a dimensionally-identical placeholder, "unloading" heavy elements (like videos) while preserving layout.
- Performance Boost: Unloads off-screen content to free up resources.
- Layout Stability: Replaces hidden content with a correctly-sized placeholder.
- Event-Driven: Emits events when its content becomes visible or hidden:
on-load
: Fired when the component's content becomes visible and is rendered.before-unload
: Fired in the same tick that the component's content starts to become hidden.on-unload
: Fired in the next tick after the component's content has become hidden and replaced by the placeholder.
Example:
<Ghost @on-load="handleLoad" @before-unload="handleBeforeUnload" @on-unload="handleUnload">
<div style="height: 300px; background-color: lightblue;">
This content will be replaced when not visible.
</div>
</Ghost>
npm install vue-infinity
Explore the live demo here: https://tewolde.co/vueInfinity/
To run the playground application locally:
npm run playground
Apache 2.0 License - https://opensource.org/licenses/Apache-2.0