npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@usefy/use-interval

v0.25.1

Published

A React hook for declarative setInterval with automatic cleanup and start/stop controls

Readme


Overview

@usefy/use-interval is a React hook that wraps setInterval for declarative, safe, repeated execution. It cleans up automatically on unmount, keeps the latest callback without restarting the timer, disables via a null delay, and exposes start/stop/toggle controls plus an isRunning flag.

Part of the @usefy ecosystem — a collection of production-ready React hooks designed for modern applications.

Why use-interval?

  • 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 without restarting the interval
  • Conditional Execution — Pass null/undefined to disable the timer
  • Full Controlstart, stop, toggle, and an isRunning flag
  • Immediate Option — Optionally run once immediately, then at each interval
  • StrictMode Safe — Symmetric setup/teardown; no leaked or duplicated timers
  • SSR Compatible — Works seamlessly with Next.js, Remix, and other SSR frameworks

Installation

# npm
npm install @usefy/use-interval

# yarn
yarn add @usefy/use-interval

# pnpm
pnpm add @usefy/use-interval

Peer Dependencies

This package requires React 18 or 19:

{
  "peerDependencies": {
    "react": "^18.0.0 || ^19.0.0"
  }
}

Quick Start

import { useState } from "react";
import { useInterval } from "@usefy/use-interval";

function Clock() {
  const [time, setTime] = useState(() => new Date());

  useInterval(() => {
    setTime(new Date());
  }, 1000);

  return <div>{time.toLocaleTimeString()}</div>;
}

API Reference

useInterval(callback, delay, options?)

Runs callback every delay milliseconds until unmount or stopped.

Parameters

| Parameter | Type | Description | | ---------- | ----------------------------- | --------------------------------------------------------------- | | callback | () => void | Function to run on each tick (always the latest reference) | | delay | number \| null \| undefined | Interval in ms, or null/undefined to disable | | options | UseIntervalOptions | { immediate?: boolean; autoStart?: boolean } |

Options

| Option | Type | Default | Description | | ----------- | --------- | ------- | ------------------------------------------------------------------- | | immediate | boolean | false | Run the callback immediately on (re)start, then at each interval | | autoStart | boolean | true | Start automatically on mount; when false, call start() |

Returns UseIntervalReturn

| Property | Type | Description | | ----------- | ------------ | ------------------------------------------------------------- | | start | () => void | Start the interval (idempotent while already running) | | stop | () => void | Stop the interval (idempotent while already stopped) | | toggle | () => void | Toggle between running and stopped | | isRunning | boolean | Whether the interval is ticking (started and valid delay) |


Examples

Polling

import { useState } from "react";
import { useInterval } from "@usefy/use-interval";

function Dashboard() {
  const [data, setData] = useState<Data | null>(null);

  useInterval(() => {
    fetchData().then(setData);
  }, 5000);

  return <pre>{JSON.stringify(data, null, 2)}</pre>;
}

Countdown (disable via null)

import { useState } from "react";
import { useInterval } from "@usefy/use-interval";

function Countdown() {
  const [count, setCount] = useState(10);

  // Passing null once count hits 0 stops the interval.
  useInterval(() => setCount((c) => c - 1), count > 0 ? 1000 : null);

  return <div>Countdown: {count}</div>;
}

Pause / Resume

import { useInterval } from "@usefy/use-interval";

function Ticker() {
  const { toggle, isRunning } = useInterval(() => {
    console.log("tick");
  }, 1000);

  return (
    <button onClick={toggle}>{isRunning ? "Pause" : "Resume"}</button>
  );
}

Manual Control (autoStart: false)

import { useInterval } from "@usefy/use-interval";

function AutoRefresh() {
  const { start, stop, isRunning } = useInterval(() => refresh(), 3000, {
    autoStart: false,
  });

  return (
    <div>
      <button onClick={start} disabled={isRunning}>
        Start
      </button>
      <button onClick={stop} disabled={!isRunning}>
        Stop
      </button>
    </div>
  );
}

Immediate Execution

import { useInterval } from "@usefy/use-interval";

function Logger() {
  // Runs once immediately, then every 2 seconds.
  useInterval(() => log(`ping @ ${Date.now()}`), 2000, { immediate: true });
  return null;
}

TypeScript

import {
  useInterval,
  type UseIntervalReturn,
  type UseIntervalOptions,
  type IntervalDelay,
  type UseIntervalCallback,
} from "@usefy/use-interval";

const { start, stop, toggle, isRunning }: UseIntervalReturn = useInterval(
  () => {},
  1000
);

Edge Cases

| Scenario | Behavior | | ---------------------------- | ---------------------------------------------------------- | | delay < 0 | Clamped to 0 (browser clamps a 0ms interval to ~4ms) | | delay === 0 | Runs as fast as the environment allows | | delay === null/undefined | Interval disabled, isRunning is false | | Unmount | Interval cleared automatically | | Callback changes | Interval keeps running; the latest callback is used | | start() while running | No effect (idempotent) — no duplicate intervals |


Performance

start, stop, and toggle are memoized with useCallback for stable references, and the callback is read through a ref so changing it never re-subscribes the interval.


Testing

This package maintains comprehensive test coverage to ensure reliability and stability.

Test Coverage

📊 View Detailed Coverage Report (GitHub Pages)


License

MIT © mirunamu

This package is part of the usefy monorepo.