Skip to main content
You are viewing Pre-Alpha documentation.
Vuetify0 Logo
Theme
Mode
Palettes
Accessibility
Vuetify One

Sign in to Vuetify One

Access premium tools across the Vuetify ecosystem — Bin, Play, Studio, and more.

Not a subscriber? See what's included

useRaf

A composable for scope-disposed safe requestAnimationFrame with automatic cleanup.


IntermediateMar 21, 2026

Usage

The useRaf composable wraps requestAnimationFrame with a cancel-then-request pattern that deduplicates rapid calls. It returns a callable function that requests an animation frame, automatically canceling any pending frame.

useRaf
<script setup lang="ts">
  import { useRaf } from '@vuetify/v0'
  import { ref } from 'vue'

  const position = ref(0)

  const updatePosition = useRaf((timestamp) => {
    position.value = Math.sin(timestamp / 1000) * 100
  })

  // Call to request frame (cancels pending)
  updatePosition()

  // Manual cancel if needed
  updatePosition.cancel()

  // Check if frame is pending
  console.log(updatePosition.isActive.value)
</script>

Examples

Scroll: 0px
Progress: 0%
RAF updates: 0

Item 1

Scroll to see RAF throttling in action

Item 2

Scroll to see RAF throttling in action

Item 3

Scroll to see RAF throttling in action

Item 4

Scroll to see RAF throttling in action

Item 5

Scroll to see RAF throttling in action

Item 6

Scroll to see RAF throttling in action

Item 7

Scroll to see RAF throttling in action

Item 8

Scroll to see RAF throttling in action

Item 9

Scroll to see RAF throttling in action

Item 10

Scroll to see RAF throttling in action

Item 11

Scroll to see RAF throttling in action

Item 12

Scroll to see RAF throttling in action

Item 13

Scroll to see RAF throttling in action

Item 14

Scroll to see RAF throttling in action

Item 15

Scroll to see RAF throttling in action

Item 16

Scroll to see RAF throttling in action

Item 17

Scroll to see RAF throttling in action

Item 18

Scroll to see RAF throttling in action

Item 19

Scroll to see RAF throttling in action

Item 20

Scroll to see RAF throttling in action

Scroll rapidly - notice how RAF updates are throttled to one per frame

<script setup lang="ts">
  import { useRaf } from '@vuetify/v0'
  import { shallowRef, useTemplateRef } from 'vue'

  const containerRef = useTemplateRef<HTMLElement>('container')
  const scrollTop = shallowRef(0)
  const scrollPercent = shallowRef(0)
  const updateCount = shallowRef(0)

  // useRaf deduplicates rapid scroll events to one update per frame
  const updateScroll = useRaf(() => {
    if (!containerRef.value) return

    const el = containerRef.value
    scrollTop.value = Math.round(el.scrollTop)
    scrollPercent.value = Math.round(
      (el.scrollTop / (el.scrollHeight - el.clientHeight)) * 100,
    )
    updateCount.value++
  })

  function onScroll () {
    // Each scroll event triggers a RAF request
    // Rapid events are deduplicated - only one callback per frame
    updateScroll()
  }
</script>

<template>
  <div class="flex flex-col gap-4">
    <div class="flex gap-4 text-sm">
      <div class="px-3 py-1.5 rounded bg-surface-variant text-on-surface-variant">
        Scroll: <span class="font-mono">{{ scrollTop }}px</span>
      </div>
      <div class="px-3 py-1.5 rounded bg-surface-variant text-on-surface-variant">
        Progress: <span class="font-mono">{{ scrollPercent }}%</span>
      </div>
      <div class="px-3 py-1.5 rounded bg-primary text-on-primary">
        RAF updates: <span class="font-mono">{{ updateCount }}</span>
      </div>
    </div>

    <!-- Progress bar -->
    <div class="h-2 bg-surface-variant rounded-full overflow-hidden">
      <div
        class="h-full bg-primary transition-all duration-75"
        :style="{ width: `${scrollPercent}%` }"
      />
    </div>

    <div
      ref="container"
      class="h-48 overflow-y-auto border border-divider rounded-lg"
      @scroll="onScroll"
    >
      <div class="p-4 space-y-4">
        <div
          v-for="i in 20"
          :key="i"
          class="p-4 rounded bg-surface-variant"
        >
          <p class="font-medium text-on-surface">Item {{ i }}</p>
          <p class="text-sm text-on-surface-variant">
            Scroll to see RAF throttling in action
          </p>
        </div>
      </div>
    </div>

    <p class="text-xs text-on-surface-variant text-center">
      Scroll rapidly - notice how RAF updates are throttled to one per frame
    </p>
  </div>
</template>

Key Features

Cancel-Then-Request Pattern

Each call cancels any pending frame before requesting a new one. This deduplicates rapid calls, ensuring only the latest request executes:

ts
const update = useRaf(() => {
  // This callback only runs once per frame
})

// These rapid calls result in only ONE frame callback
update()
update()
update()

Automatic Cleanup

The composable automatically cancels pending frames when the Vue scope is disposed (component unmount, effect scope stop):

ts
// No manual cleanup needed - handled automatically
const update = useRaf(callback)

SSR Safe

The composable is a no-op in non-browser environments. isActive always returns false during SSR.

Architecture

useRaf provides a lightweight wrapper around requestAnimationFrame:

useRaf Hierarchy

Use controls to zoom and pan. Click outside or press Escape to close.

useRaf Hierarchy

API Reference

The following API details are for the useRaf composable.

Functions

useRaf

(callback: (timestamp: DOMHighResTimeStamp) => void) => UseRafReturn

Scope-disposed safe requestAnimationFrame.

Properties

isActive

Readonly<Ref<boolean, boolean>>

Whether an animation frame is pending

Methods

cancel

() => void

Cancel pending animation frame

Was this page helpful?

© 2016-1970 Vuetify, LLC
Ctrl+/