Skip to main content
Runs a callback on a managed interval and exposes imperative controls. The callback reference stays fresh across renders without restarting the interval.

Signature

function useInterval(
  fn: () => void,
  interval: number,
  options?: UseIntervalOptions
): UseIntervalReturnValue

Parameters

fn
() => void
required
Callback to execute on each tick.
interval
number
required
Tick interval in milliseconds.
options
UseIntervalOptions
Optional behavior flags.

Returns

controls
UseIntervalReturnValue
An object with interval control methods and state.

Usage

Basic counter

import { useInterval } from '@kuzenbo/hooks';
import { useState } from 'react';

function Counter() {
  const [count, setCount] = useState(0);
  const { start, stop, active } = useInterval(() => {
    setCount((c) => c + 1);
  }, 1000);

  return (
    <div>
      <p>Count: {count}</p>
      <button onClick={start} disabled={active}>Start</button>
      <button onClick={stop} disabled={!active}>Stop</button>
    </div>
  );
}

Auto-start interval

import { useInterval } from '@kuzenbo/hooks';
import { useState } from 'react';

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

  const { stop } = useInterval(
    () => setTime(new Date()),
    1000,
    { autoInvoke: true }
  );

  return (
    <div>
      <p>{time.toLocaleTimeString()}</p>
      <button onClick={stop}>Stop clock</button>
    </div>
  );
}

Toggle interval

import { useInterval } from '@kuzenbo/hooks';
import { useState } from 'react';

function Blinker() {
  const [visible, setVisible] = useState(true);
  const { toggle, active } = useInterval(() => {
    setVisible((v) => !v);
  }, 500);

  return (
    <div>
      <div style={{ opacity: visible ? 1 : 0 }}>Blinking text</div>
      <button onClick={toggle}>
        {active ? 'Stop' : 'Start'} blinking
      </button>
    </div>
  );
}

Data polling

import { useInterval } from '@kuzenbo/hooks';
import { useState, useCallback } from 'react';

function LiveData() {
  const [data, setData] = useState(null);
  const [error, setError] = useState(null);

  const fetchData = useCallback(async () => {
    try {
      const response = await fetch('/api/data');
      const json = await response.json();
      setData(json);
      setError(null);
    } catch (err) {
      setError(err.message);
    }
  }, []);

  const { start, stop, active } = useInterval(fetchData, 5000, {
    autoInvoke: true
  });

  return (
    <div>
      {error && <p>Error: {error}</p>}
      {data && <pre>{JSON.stringify(data, null, 2)}</pre>}
      <button onClick={active ? stop : start}>
        {active ? 'Stop' : 'Start'} polling
      </button>
    </div>
  );
}

Build docs developers (and LLMs) love