A React hook for declarative setTimeout with automatic cleanup and controls
npm install @usefy/use-timeout

A lightweight, type-safe React hook for declarative setTimeout with automatic cleanup
Installation •
Quick Start •
API Reference •
Examples •
License
---
@usefy/use-timeout is a powerful React hook that wraps setTimeout for declarative and safe delayed execution. It provides automatic cleanup on unmount, reset/clear controls, and conditional execution with null delay.
Part of the @usefy ecosystem — a collection of production-ready React hooks designed for modern applications.
- Zero Dependencies — Pure React implementation with no external dependencies
- TypeScript First — Full type safety with exported interfaces
- Memory Safe — Automatic cleanup on unmount prevents memory leaks
- Stale Closure Free — Always executes the latest callback reference
- Conditional Execution — Pass null to disable the timer
- Full Control — Reset and clear functions for timer management
- SSR Compatible — Works seamlessly with Next.js, Remix, and other SSR frameworks
- Lightweight — Minimal bundle footprint (~400B minified + gzipped)
- Well Tested — Comprehensive test coverage with Vitest
---
``bashnpm
npm install @usefy/use-timeout
$3
This package requires React 18 or 19:
`json
{
"peerDependencies": {
"react": "^18.0.0 || ^19.0.0"
}
}
`---
Quick Start
`tsx
import { useTimeout } from "@usefy/use-timeout";function Toast({ message }: { message: string }) {
const [show, setShow] = useState(true);
useTimeout(() => {
setShow(false);
}, 3000);
return show ?
{message} : null;
}
`---
API Reference
$3
A hook that executes a callback after a specified delay with automatic cleanup and controls.
#### Parameters
| Parameter | Type | Description |
| ---------- | ----------------------------- | ------------------------------------------------------- |
|
callback | () => void | Function to execute after the delay |
| delay | number \| null \| undefined | Delay in milliseconds, or null/undefined to disable |#### Returns
UseTimeoutReturn| Property | Type | Description |
| ----------- | ------------ | ----------------------------------------- |
|
reset | () => void | Restart the timer from the beginning |
| clear | () => void | Cancel the timer (callback won't execute) |
| isPending | boolean | Whether the timer is currently pending |---
Examples
$3
`tsx
import { useState } from "react";
import { useTimeout } from "@usefy/use-timeout";function AutoDismissToast({ message }: { message: string }) {
const [show, setShow] = useState(true);
useTimeout(() => {
setShow(false);
}, 3000);
return show ?
{message} : null;
}
`$3
`tsx
import { useState } from "react";
import { useTimeout } from "@usefy/use-timeout";function Editor() {
const [content, setContent] = useState("");
const [isSaving, setIsSaving] = useState(false);
const { reset } = useTimeout(() => {
saveToServer(content);
setIsSaving(false);
}, 2000);
const handleChange = (e: React.ChangeEvent) => {
setContent(e.target.value);
setIsSaving(true);
reset(); // Reset timer on every keystroke
};
return (
{isSaving ? "Saving..." : "Saved"}
);
}
`$3
`tsx
import { useTimeout } from "@usefy/use-timeout";function SessionManager({ isLoggedIn }: { isLoggedIn: boolean }) {
useTimeout(
() => {
logout();
alert("Session expired due to inactivity");
},
isLoggedIn ? 30 60 1000 : null // 30 minutes, disabled when logged out
);
return
Your session is active;
}
`$3
`tsx
import { useState } from "react";
import { useNavigate } from "react-router-dom";
import { useTimeout } from "@usefy/use-timeout";function PaymentSuccess() {
const navigate = useNavigate();
const [countdown, setCountdown] = useState(5);
const { clear } = useTimeout(() => {
navigate("/dashboard");
}, 5000);
// Countdown effect
useInterval(() => {
setCountdown((c) => c - 1);
}, 1000);
return (
Payment Successful!
Redirecting to dashboard in {countdown} seconds...
);
}
`$3
`tsx
import { useTimeout } from "@usefy/use-timeout";function TimerStatus() {
const { isPending, reset, clear } = useTimeout(() => {
console.log("Timer executed!");
}, 5000);
return (
Status: {isPending ? "Pending..." : "Completed or Cancelled"}
);
}
`$3
`tsx
import { useState, useCallback } from "react";
import { useTimeout } from "@usefy/use-timeout";function NotificationBanner() {
const [notification, setNotification] = useState(null);
const { reset } = useTimeout(
() => {
setNotification(null);
},
notification ? 4000 : null
);
const showNotification = useCallback(
(message: string) => {
setNotification(message);
reset();
},
[reset]
);
return (
<>
{notification && (
{notification}
)}
>
);
}
`---
TypeScript
This hook is written in TypeScript and exports all necessary types.
`tsx
import {
useTimeout,
type UseTimeoutReturn,
type TimeoutDelay,
type UseTimeoutCallback,
} from "@usefy/use-timeout";// Return type is fully typed
const { reset, clear, isPending }: UseTimeoutReturn = useTimeout(() => {
console.log("Executed!");
}, 3000);
// reset: () => void
// clear: () => void
// isPending: boolean
`---
Performance
All functions returned by the hook are memoized using
useCallback, ensuring stable references across re-renders. The callback reference is maintained with useRef to prevent timer resets when the callback changes.`tsx
const { reset, clear } = useTimeout(callback, 1000);// These references remain stable across renders
useEffect(() => {
// Safe to use as dependencies
}, [reset, clear]);
// Changing callback doesn't reset the timer
// Only the latest callback will be executed
`---
Edge Cases
| Scenario | Behavior |
| -------------------------------- | ----------------------------------------- |
|
delay < 0 | Treated as 0 (immediate execution) |
| delay === 0 | Executes on next event loop |
| delay === null | Timer disabled, isPending is false |
| delay === undefined | Timer disabled, isPending is false |
| Unmount before delay | Callback not executed, timer cleaned up |
| Callback changes | Timer continues, latest callback executed |
| reset() when delay is null` | No effect |---
This package maintains comprehensive test coverage to ensure reliability and stability.
📊 View Detailed Coverage Report (GitHub Pages)
---
MIT © mirunamu
This package is part of the usefy monorepo.
---
Built with care by the usefy team