Skip to content

usePullToRefresh ​

usePullToRefresh is a headless Vue composable for the pull to refresh gesture on the window or any scroll container.

Introduction ​

usePullToRefresh tracks the pull to refresh gesture and gives you its state (distance, progress, ready, refreshing) to build your own indicator. It powers MazPullToRefresh: use the component for a ready-made indicator, the composable when you need full control over the rendering.

The pull only starts when the target is scrolled to the top, follows a rubber band beyond the threshold, ignores horizontal and multi-touch gestures, and prevents the native bounce while pulling.

Basic usage ​

vue
<script lang="ts" setup>
import { usePullToRefresh } from 'maz-ui/composables'
import { useTemplateRef } from 'vue'

const scroller = useTemplateRef<HTMLElement>('scroller')

const { distance, progress, isReady, isRefreshing } = usePullToRefresh({
  target: scroller,
  onRefresh: () => fetchData(),
})
</script>

<template>
  <div ref="scroller" class="maz:h-screen maz:overflow-y-auto">
    <div class="maz:flex maz:items-end maz:justify-center maz:overflow-hidden" :style="{ height: `${distance}px` }">
      <span v-if="isRefreshing">Refreshing…</span>
      <span v-else-if="isReady">Release to refresh</span>
      <span v-else :style="{ opacity: progress }">Pull to refresh</span>
    </div>

    <p>Content</p>
  </div>
</template>

Without target, the gesture is tracked on the window.

Options ​

OptionTypeDefaultDescription
onRefresh() => unknown-Called on release past the threshold, or by refresh()
targetMaybeRefOrGetter<HTMLElement | Window | null>windowScroll container receiving the gesture
thresholdMaybeRefOrGetter<number>80Pull distance (px, after resistance) needed to trigger a refresh
maxDistanceMaybeRefOrGetter<number>threshold * 1.6Upper bound of the rubber band
resistanceMaybeRefOrGetter<number>0.5Ratio between the finger movement and the pull distance
minDurationMaybeRefOrGetter<number>400Minimum duration (ms) of the refreshing state
hapticMaybeRefOrGetter<boolean>trueVibrate briefly when the threshold is reached
disabledMaybeRefOrGetter<boolean>falseDisable the gesture
onError(error: unknown) => void-Called when onRefresh throws or rejects (the error is also exposed in error)

Return ​

PropertyTypeDescription
distanceReadonly<Ref<number>>Current pull distance in px
progressComputedRef<number>Progress towards the threshold, from 0 to 1
isPullingReadonly<Ref<boolean>>The user is pulling
isReadyComputedRef<boolean>Releasing now triggers a refresh
isRefreshingReadonly<Ref<boolean>>onRefresh is running
errorReadonly<Ref<unknown>>Error of the last refresh, reset on each refresh
refresh() => Promise<void>Run the refresh programmatically (for a keyboard-usable button)
start() => voidAttach the listeners (done automatically on mount)
stop() => voidDetach the listeners (done automatically on unmount)

Notes ​

  • Inside a component, listeners are attached on mount and removed when the scope is disposed. Outside of a component, they are attached right away.
  • When target changes (for example a ref resolved later), the listeners move to the new element.
  • refresh() never throws: errors go to error and onError. Calling it while a refresh is running does nothing.
  • SSR safe: nothing touches the DOM before mount.