vue-smart-scroll
v0.1.5
Published
Modular Vue 3 infinite scroll and pull-to-refresh components with composables — tree-shakeable, SSR-safe, style-agnostic.
Maintainers
Readme
vue-smart-scroll
Production-oriented Vue 3 infinite scroll and pull-to-refresh primitives: three tree-shakeable components plus headless composables. Written in TypeScript, built with Vite, no UI framework, SSR-safe (browser APIs run after mount), and style-agnostic aside from small scoped defaults you can override.
Installation
npm install vue-smart-scrollPeer dependency: Vue ^3.5 (uses useId for SVG defs).
Styles
Default spinner / layout helpers ship as a small CSS file:
import 'vue-smart-scroll/style.css'You can skip this import and style slots yourself.
Quick start
<script setup lang="ts">
import { ref } from 'vue'
import { SmartScrollContainer } from 'vue-smart-scroll'
import 'vue-smart-scroll/style.css'
const items = ref<number[]>([1, 2, 3])
const hasMore = ref(true)
async function loadMore() {
await new Promise((r) => setTimeout(r, 400))
const n = items.value.length
items.value.push(n + 1, n + 2, n + 3)
if (items.value.length > 30) hasMore.value = false
}
async function onRefresh() {
await new Promise((r) => setTimeout(r, 500))
items.value = [1, 2, 3]
hasMore.value = true
}
</script>
<template>
<SmartScrollContainer
style="height: 70vh"
:pull-to-refresh="true"
:load-more="loadMore"
:on-refresh="onRefresh"
:has-more="hasMore"
>
<div v-for="i in items" :key="i" style="padding: 12px; border-bottom: 1px solid #eee">
Row {{ i }}
</div>
</SmartScrollContainer>
</template>Run the bundled demo:
npm install
npm run devComponents
SmartInfiniteScroll
IntersectionObserver-driven loader with slots for loader, error (with retry + error), and end.
| Prop | Type | Default | Description |
|------|------|---------|-------------|
| loadMore | () => Promise<void> | — | Fetches the next page. |
| hasMore | boolean | — | false when the server has no next page. |
| threshold | number \| number[] | 0 | IntersectionObserver threshold. |
| disabled | boolean | false | Disables loads. |
| immediate | boolean | false | Runs loadMore once on mount. |
| autoLoad | boolean | true | false = no observer; call triggerLoadMore() (exposed) yourself. |
| root | Element \| null | — | Scroll root; null = viewport. If omitted inside SmartPullToRefresh, the internal scroll element is injected. |
| rootMargin | string | '0px' | Passed to IntersectionObserver. |
Events: load, error (payload: unknown), end.
Expose: triggerLoadMore(), retry().
SmartPullToRefresh
Touch pull with rubber-band resistance, optional window mode, and an indicator slot.
| Prop | Type | Default | Description |
|------|------|---------|-------------|
| onRefresh | () => Promise<void> | — | Refresh handler. |
| threshold | number | 42 | Pull distance (px) to commit refresh. |
| maxPullDistance | number | 90 | Visual cap (px). |
| disabled | boolean | false | Disables gestures. |
| useWindowScroll | boolean | false | Uses window scroll + document.documentElement for touch. |
Events: refresh (when a refresh starts), pulling (distance), release ({ distance, willRefresh }).
Expose: scrollEl (internal scroller ref when not using window mode).
SmartScrollContainer
Composes the two features: toggles infinite, pullToRefresh, and useWindowScroll, forwards slots (loader, error, end, indicator).
Composables
Use independently for virtualized lists or custom markup.
useInfiniteScroll(options)— sentinel ref, phases (idle|loading|error|complete),triggerLoadMore,retry, IntersectionObserver wiring,immediate/autoLoad.usePullToRefresh(options)—pullDistance,isRefreshing,attach()/detach()(binds passivetouchstartand non-passivetouchmovewhen pulling).useScrollPosition({ target, throttle })— reactivescrollTop/scrollLeftwith passivescrolllisteners and optional rAF coalescing.
Injection
SmartPullToRefresh provides VSS_SCROLL_ROOT so nested SmartInfiniteScroll can omit root and still observe against the correct scroller.
Performance notes
touchstartuses passive listeners;touchmoveis non-passive only on the bound element when overscrolling from the top (needed forpreventDefault).- Pull distance notifications are coalesced with
requestAnimationFrame. - Observers and DOM listeners are removed on unmount; guards avoid running in SSR.
Package exports
Named exports from vue-smart-scroll: components, composables, types (LoadMoreFn, RefreshFn, InfiniteScrollPhase, …), helpers (readScrollTop, rubberBandResistance, …), and VSS_SCROLL_ROOT.
