use-countdown
Count down from a number of ticks to zero.
| Package | @vizejs/composable/use-countdown |
| Own the source | vize lib pull composable:use-countdown |
| Runtime exports | useCountdown |
| 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 { useCountdown } from "@vizejs/composable/use-countdown";
Try it
Wait before resending a sign-in code
Count down a cooldown after an action, keep the button disabled until it ends, and allow another request afterwards.
What to observe: Send code starts a 30-second cooldown and disables the button while the remaining seconds count down. When it reaches zero the button reads Resend code again and the message counts the codes sent.
start() resets to the initial count and begins ticking; isActive is true while the timer runs. Call useCountdown inside setup so its interval is cleared with the component. Pass intervalMs for a different tick length.
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 } from "vue";
import { useCountdown } from "@vizejs/composable/use-countdown";
const sent = ref(0);
const { remaining, isActive, start } = useCountdown(30);
function sendCode() {
sent.value += 1;
start();
}
</script>
<template>
<div class="composable-example">
<p>Let a reader request a new sign-in code, then wait before asking again.</p>
<button type="button" :disabled="isActive" @click="sendCode">
{{ sent === 0 ? "Send code" : "Resend code" }}
</button>
<output aria-live="polite">{{
sent === 0
? "No code sent yet."
: isActive
? `Code sent. Resend available in ${remaining} s.`
: `Code sent ${sent} times. You can request another one.`
}}</output>
</div>
</template>
Runtime contract
| Utility | Category | Stability | SSR | Hydration | Cleanup | Targets | Host globals | Uses |
|---|---|---|---|---|---|---|---|---|
useCountdown |
timing | experimental | deterministic-fallback | stable | caller, reactive-scope | web, server, worker, native, desktop, terminal | globalThis, window |
useIntervalFn |
API
useCountdown
Count down from a number of ticks to zero. Builds on useIntervalFn: pause/resume keep the remaining count, start and reset restore it, and the timer pauses itself at zero after calling onComplete. The initial count is reactive only for later start()/reset() calls without an argument. Server rendering is hydration-stable (no timer, remaining stays at the initial count).
function useCountdown( initialCount: MaybeRefOrGetter<number>, options: UseCountdownOptions = {}, ): CountdownControls
const { remaining, start } = useCountdown(60, { onComplete: resendAvailable });
start();
Types
UseCountdownOptions
Options for useCountdown.
| Member | Type | Description |
|---|---|---|
intervalMs? |
MaybeRefOrGetter<number> |
Milliseconds between two decrements. Reactive. |
immediate? |
boolean |
Start counting down as soon as the composable is created. |
onTick? |
(remaining: number) => void |
Invoked after every decrement with the remaining count. |
onComplete? |
() => void |
Invoked once when the count reaches zero. |
runOnServer? |
boolean |
Starts host timers when no browser window is available. |
scheduler? |
IntervalScheduler |
Repeating timer host. |
CountdownControls
State and controls returned by useCountdown.
| Member | Type | Description |
|---|---|---|
isActive |
Readonly<ShallowRef<boolean>> |
Whether the timer is logically running. On the server (without runOnServer) this reflects the requested state without starting a host timer, so server and client render the same initial value. |
pause |
() => void |
Stop ticking. Idempotent. |
resume |
() => void |
Start (or keep) ticking. Idempotent. |
remaining |
Readonly<ShallowRef<number>> |
Remaining whole ticks; never negative. |
isComplete |
ComputedRef<boolean> |
Whether the countdown reached zero. |
start |
(count?: number) => void |
Reset to count (default: the initial count) and start ticking. |
reset |
(count?: number) => void |
Pause and reset to count (default: the initial count). |
stop |
() => void |
Pause and jump to zero without firing onComplete. |