Skip to main content

Overview

The useInterval hook calls a callback function on a repeating interval. It handles cleanup automatically and can be paused by passing null as the delay.

Signature

function useInterval(callback: () => void, delay: number | null): void

Parameters

callback
() => void
required
Function to call on each tick of the interval
delay
number | null
required
Interval in milliseconds between each callback execution. Pass null to pause the interval.

Examples

Basic counter

import { useState } from 'react';
import { useInterval } from '@kivora/react';

function Counter() {
  const [count, setCount] = useState(0);
  const [running, setRunning] = useState(true);

  useInterval(() => {
    setCount(c => c + 1);
  }, running ? 1000 : null);

  return (
    <div>
      <p>Count: {count}</p>
      <button onClick={() => setRunning(!running)}>
        {running ? 'Pause' : 'Start'}
      </button>
    </div>
  );
}

Clock component

import { useState } from 'react';
import { useInterval } from '@kivora/react';

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

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

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

Auto-refresh data

import { useState } from 'react';
import { useInterval } from '@kivora/react';

function LiveData() {
  const [data, setData] = useState(null);
  const [isActive, setIsActive] = useState(true);

  const fetchData = async () => {
    const response = await fetch('/api/data');
    const json = await response.json();
    setData(json);
  };

  useInterval(() => {
    fetchData();
  }, isActive ? 5000 : null);

  return (
    <div>
      <button onClick={() => setIsActive(!isActive)}>
        {isActive ? 'Stop Refresh' : 'Start Refresh'}
      </button>
      <pre>{JSON.stringify(data, null, 2)}</pre>
    </div>
  );
}

Progress animation

import { useState } from 'react';
import { useInterval } from '@kivora/react';

function ProgressBar() {
  const [progress, setProgress] = useState(0);

  useInterval(() => {
    setProgress(p => {
      if (p >= 100) return 0; // Reset when complete
      return p + 1;
    });
  }, 50);

  return (
    <div style={{ width: '100%', backgroundColor: '#eee' }}>
      <div
        style={{
          width: `${progress}%`,
          height: '20px',
          backgroundColor: '#4CAF50',
          transition: 'width 0.05s linear'
        }}
      />
    </div>
  );
}

Polling with pause

import { useState, useEffect } from 'react';
import { useInterval } from '@kivora/react';

function DataPoller() {
  const [data, setData] = useState([]);
  const [isPaused, setIsPaused] = useState(false);

  const pollData = async () => {
    const res = await fetch('/api/updates');
    const updates = await res.json();
    setData(prev => [...prev, ...updates]);
  };

  // Initial fetch
  useEffect(() => {
    pollData();
  }, []);

  // Poll every 3 seconds when not paused
  useInterval(() => {
    pollData();
  }, isPaused ? null : 3000);

  return (
    <div>
      <button onClick={() => setIsPaused(!isPaused)}>
        {isPaused ? 'Resume' : 'Pause'} Polling
      </button>
      <ul>
        {data.map((item, i) => <li key={i}>{item}</li>)}
      </ul>
    </div>
  );
}

Use Cases

  • Timers & counters: Count down or up at regular intervals
  • Clocks: Display current time with second precision
  • Auto-refresh: Periodically fetch new data from an API
  • Animations: Create frame-based animations
  • Polling: Check for updates at regular intervals
  • Game loops: Update game state on a fixed schedule

Notes

  • The interval automatically cleans up when the component unmounts
  • Pass null as the delay to pause the interval without unmounting
  • The callback ref is updated on every render to always use the latest version
  • This hook is based on Dan Abramov’s popular useInterval implementation

Build docs developers (and LLMs) love