use-throttled
Create a readonly throttled view of a reactive source.
| Package | @vizejs/composable/use-throttled |
| Own the source | vize lib pull composable:use-throttled |
| Runtime exports | useThrottled |
| Gzip budget | 2560 B |
Minimal setup
Use a Vue 3.5+ project with Vue SFC compilation configured (see Vite+ integration).
vp install @vizejs/composable
Import the entry directly. Call lifecycle-bound utilities inside <script setup> so cleanup follows the component scope; consult the runtime contract for this entry's SSR and hydration behavior.
import { useThrottled } from "@vizejs/composable/use-throttled";
Try it
Preview a release title while editing
Keep the latest release-title draft editable while limiting how often its local preview updates.
What to observe: Change the title once to update the preview immediately. Further edits in the same 800 ms window wait; the newest draft appears when that window ends. Update preview now applies the waiting draft immediately. Cancel waiting update keeps both your draft and the last accepted preview; the next edit starts a fresh window.
The example uses useThrottled's default leading and trailing edges and its real timer. pending describes a queued update, not whether the draft equals the preview. flush() applies waiting work and closes its window; cancel() discards waiting work and closes its window without changing the draft. SSR mirrors the source without starting timers. Call inside setup so scope disposal releases the watcher and timer. This local preview neither saves nor publishes a title; the package remains experimental.
Compare the initial and interacted states


Copy the example
Save this complete SFC in your Vue project. Its public imports and source are the same ones executed in the preview.
<script setup lang="ts">
import { ref, useId } from "vue";
import { useThrottled } from "@vizejs/composable/use-throttled";
const draft = ref("Vize release notes");
const titleId = useId();
const { throttled, pending, flush, cancel } = useThrottled(draft, 800);
const actionMessage = ref("Compare the draft and its preview.");
function showLatest(): void {
if (flush()) actionMessage.value = "Newest draft shown now.";
}
function cancelWaiting(): void {
if (cancel()) actionMessage.value = "Waiting preview update canceled; draft kept.";
}
</script>
<template>
<div class="composable-example">
<label :for="titleId">Draft release title</label>
<input :id="titleId" v-model="draft" type="text" />
<p>
The first edit updates the preview immediately. Further edits in the same 800 ms window wait.
</p>
<p>Release title preview</p>
<output aria-label="Release title preview" aria-live="polite">{{ throttled }}</output>
<p role="status" aria-label="Preview update status">
{{ pending ? "Newest draft is waiting." : "No update is waiting." }}
</p>
<div class="example-actions">
<button type="button" :disabled="!pending" @click="showLatest">Update preview now</button>
<button type="button" :disabled="!pending" @click="cancelWaiting">
Cancel waiting update
</button>
</div>
<p aria-live="polite">{{ actionMessage }}</p>
</div>
</template>
Runtime contract
| Utility | Category | Stability | SSR | Hydration | Cleanup | Targets | Host globals | Uses |
|---|---|---|---|---|---|---|---|---|
useThrottled |
timing | experimental | deterministic-fallback | stable | caller, reactive-scope | web, server, worker, native, desktop, terminal | globalThis, window |
tryOnScopeDispose |
API
useThrottled
Create a readonly throttled view of a reactive source. Changes are observed with flush: "sync", so every synchronous write counts. Outside a cooldown window, a change applies immediately when UseThrottledOptions.leading is enabled (otherwise it waits as a trailing update) and opens a window of waitMs milliseconds. Changes inside a window are collected as the trailing candidate; when the window ends with a candidate waiting, the source value current at that moment is applied and the next window opens back to back, keeping applications spaced by waitMs. A window that ends without a candidate closes silently. waitMs is reactive and is read each time a window opens; changing it never disturbs an already-open window. A wait of 0 still defers trailing updates to the next scheduler tick. Server rendering is explicit: without a browser window (and with UseThrottledOptions.runOnServer disabled) no timer ever starts and the view mirrors the source synchronously, so server-rendered output shows current values and nothing leaks. pending stays false and the controls report false in that mode. Cleanup rule: the watcher and any open window timer are released when the owning reactive scope stops; call inside an active scope. Outside one, the watcher lives as long as the source and cancel() only clears the window.
function useThrottled<Value>( source: MaybeRefOrGetter<Value>, waitMs: MaybeRefOrGetter<number>, options: UseThrottledOptions = {}, ): ThrottledControls<Value>
const scrollY = shallowRef(0);
const { throttled } = useThrottled(scrollY, 100);
scrollY.value = 40; // applied immediately (leading edge)
scrollY.value = 80; // applied when the 100ms window ends
Types
UseThrottledOptions
Options for useThrottled.
| Member | Type | Description |
|---|---|---|
leading? |
boolean |
Apply the first change of a cooldown window immediately. |
trailing? |
boolean |
Apply the newest change collected during a cooldown window when the window ends. When disabled, changes inside a window are dropped. |
runOnServer? |
boolean |
Applies the timing policy when no browser window is available. Keep this disabled during server rendering, where the throttled view mirrors the source synchronously instead of starting timers. Enable it for native, desktop, worker, and test runtimes whose scheduler is lifecycle-bound. |
scheduler? |
TimeoutScheduler |
Owns the single-shot cooldown timer. |
ThrottledControls
Reactive throttled view and controls returned by useThrottled.
| Member | Type | Description |
|---|---|---|
throttled |
Readonly<ShallowRef<Value>> |
Readonly view of the source updated at most once per cooldown window. |
pending |
Readonly<ShallowRef<boolean>> |
Whether a trailing update is waiting for the current window to end. |
cancel |
() => boolean |
Discard the waiting trailing update and close the cooldown window, so the next change starts fresh on a leading edge. |
flush |
() => boolean |
Apply the waiting trailing update immediately and close the cooldown window. Without a waiting update the window is left untouched. |