useCooldown React hook in TypeScript
v1.0.0Added in 1.0.0
A React cooldown timer hook for resend and retry buttons: wait N seconds before a code can be sent again, kept across reloads.
Press Resend code: the button locks and counts down (0:30, 0:29...) while the ring fills, then unlocks. Reload the page with a persist key and the wait continues where it was. Reset stops it at once. The label updates once a second, on the second.
useCooldown is a Pro React hook in the React hooks and utilities category, written in TypeScript for React 19. It installs as source with npx wingo-ui@latest add use-cooldown, together with usePersistedState.
Add to a project
Requirements: React 19, in Next.js (App Router or Pages), Vite or React Router. Installation guide · Next.js setup · Vite setup
npx wingo-ui@latest add use-cooldownuseCooldown is part of Pro, with 298 more Pro items: $8 a month or $150 once. 14-day refund before you download any Pro item. See plans
Usage
Import useCooldown from @/hooks/use-cooldown.
import { Button } from "@/components/ui/button"
import { useCooldown } from "@/hooks/use-cooldown"
export function ResendCode({ onResend }: { onResend: () => Promise<void> }) {
const resend = useCooldown({ seconds: 30, autoStart: true, persistKey: "otp-resend" })
return (
<Button
variant="link"
disabled={resend.active}
onClick={async () => {
await onResend()
resend.start()
}}
>
{resend.active ? `Resend code in ${resend.label}` : "Resend code"}
</Button>
)
}API
secondsdefault30numberLength of one wait in seconds. start(seconds) overrides it for a single wait (a longer lock after 3 failed attempts).
autoStartdefaultfalsebooleanStart waiting on mount, for screens that open right after a code was sent (check your email, enter the SMS code). The server and hydration render the full wait, so the label never jumps.
persistKeydefault""stringKeeps the end time in local storage under this key, so a reload does not unlock the button early. Empty keeps it in memory.
onEnd() => voidRuns once when a wait reaches zero (not on reset).
useCooldown(options?: UseCooldownOptions) => CooldownReturns { remaining (whole seconds), active, endsAt, label ("0:42"), progress (0 to 1 for a ring), start(seconds?), reset() }. It ticks with one timeout per second aligned to the end time: one re-render a second, exact after the tab sleeps.
Parts
formatCooldown
functionMinutes and zero-padded seconds: 42 is "0:42", 65 is "1:05". Signature: (seconds: number) => string.
UseCooldownOptions
typeThe options object: { seconds?: number; autoStart?: boolean; persistKey?: string; onEnd?: () => void }.
Cooldown
typeThe return value: remaining, active, endsAt, label, progress, start(seconds?) and reset().
Examples
Starts on mount
The screen opens right after the code was sent.
Survives a reload
persistKey keeps the end time in local storage; reload mid-wait.
Inline link
A 15 second wait on a link under a form.
Dependencies
Files
- hooks/use-cooldown.ts
FAQ
Is the useCooldown hook free?
The preview, props and docs are free; copying the source needs a Pro plan. See plans.
How do I install useCooldown?
Run npx wingo-ui@latest add use-cooldown. It also adds the Wingo UI item it builds on (usePersistedState). With shadcn, add the @wingo-ui registry with your API key, then run npx shadcn@latest add @wingo-ui/use-cooldown.
See also
Used in
More React hooks and utilities
Articles using this component
- React OTP Input: Paste, SMS Autofill and a Resend Timer
Component guides11 min read