Rooks
HooksBrowser APIs

useIdleDetectionApi

Detects when the user is idle using the IdleDetection API.

About

Hook to detect when user is idle using the native Idle Detection API with polyfill fallback. This hook provides comprehensive idle detection capabilities including user activity state, screen lock state, and permission management.


Examples

Basic idle detection

import { useIdleDetectionApi } from "rooks";

export default function App() {
  const { isIdle, userState, screenState, isSupported, start, stop } =
    useIdleDetectionApi({
      threshold: 60000, // 60 seconds
      autoStart: true,
      requestPermission: true,
    });

  return (
    <div>
      <h3>Idle Detection Status</h3>
      <p>API Supported: {isSupported ? "Yes" : "No"}</p>
      <p>
        User State: <strong>{userState}</strong>
      </p>
      <p>
        Screen State: <strong>{screenState}</strong>
      </p>
      <p>
        Is Idle: <strong>{isIdle ? "Yes" : "No"}</strong>
      </p>

      <button onClick={start}>Start Detection</button>
      <button onClick={stop}>Stop Detection</button>
    </div>
  );
}

With state change callbacks

import { useIdleDetectionApi } from "rooks";
import { useState } from "react";

export default function App() {
  const [lastChange, setLastChange] = useState(null);
  const [errors, setErrors] = useState([]);

  const { isIdle, userState, screenState, isPermissionGranted } =
    useIdleDetectionApi({
      threshold: 120000, // 2 minutes
      autoStart: true,
      requestPermission: true,
      onIdleChange: (state) => {
        setLastChange({
          timestamp: new Date().toLocaleTimeString(),
          ...state,
        });
      },
      onError: (error) => {
        setErrors((prev) => [...prev, error.message]);
      },
    });

  return (
    <div>
      <h3>Idle Detection with Callbacks</h3>
      <p>Current State: {isIdle ? "Idle" : "Active"}</p>
      <p>User State: {userState}</p>
      <p>Screen State: {screenState}</p>
      <p>Permission Granted: {isPermissionGranted ? "Yes" : "No"}</p>

      {lastChange && (
        <div>
          <h4>Last Change:</h4>
          <p>Time: {lastChange.timestamp}</p>
          <p>Was Idle: {lastChange.isIdle ? "Yes" : "No"}</p>
          <p>User State: {lastChange.userState}</p>
          <p>Screen State: {lastChange.screenState}</p>
        </div>
      )}

      {errors.length > 0 && (
        <div>
          <h4>Errors:</h4>
          {errors.map((error, index) => (
            <p key={index} style={{ color: "red" }}>
              {error}
            </p>
          ))}
        </div>
      )}
    </div>
  );
}

Manual control with custom threshold

import { useIdleDetectionApi } from "rooks";
import { useState } from "react";

export default function App() {
  const [customThreshold, setCustomThreshold] = useState(60000);
  const [isStarted, setIsStarted] = useState(false);

  const {
    isIdle,
    userState,
    screenState,
    isSupported,
    isPermissionGranted,
    start,
    stop,
  } = useIdleDetectionApi({
    threshold: customThreshold,
    autoStart: false, // Manual control
    requestPermission: true,
  });

  const handleStart = async () => {
    await start();
    setIsStarted(true);
  };

  const handleStop = () => {
    stop();
    setIsStarted(false);
  };

  const handleThresholdChange = (e) => {
    setCustomThreshold(parseInt(e.target.value) * 1000);
  };

  return (
    <div>
      <h3>Manual Idle Detection Control</h3>

      <div>
        <label>
          Threshold (seconds):
          <input
            type="number"
            min="60"
            value={customThreshold / 1000}
            onChange={handleThresholdChange}
            disabled={isStarted}
          />
        </label>
      </div>

      <div>
        <button onClick={handleStart} disabled={isStarted}>
          Start Detection
        </button>
        <button onClick={handleStop} disabled={!isStarted}>
          Stop Detection
        </button>
      </div>

      <div>
        <h4>Status:</h4>
        <p>API Supported: {isSupported ? "Yes" : "No"}</p>
        <p>Permission Granted: {isPermissionGranted ? "Yes" : "No"}</p>
        <p>Detection Active: {isStarted ? "Yes" : "No"}</p>
        <p>Current State: {isIdle ? "Idle" : "Active"}</p>
        <p>User State: {userState}</p>
        <p>Screen State: {screenState}</p>
      </div>
    </div>
  );
}

Parameters

ArgumentTypeDescriptionDefault value
optionsobjectConfiguration options for idle detection{}

Options

Option keyTypeDescriptionDefault value
thresholdnumberMinimum time in milliseconds before user is considered idle. Must be at least 60000ms (60 seconds) per W3C specification60000
autoStartbooleanWhether to automatically start idle detection when the hook mountsfalse
requestPermissionbooleanWhether to automatically request permission for the Idle Detection API (required for native API usage)false
onIdleChangefunctionCallback function called when idle state changes. Receives object with {isIdle, userState, screenState}undefined
onErrorfunctionCallback function called when an error occurs during idle detection setup or operation. Receives Error objectundefined

Return value

Return valueTypeDescription
isIdlebooleanWhether the user is currently idle
userStatestringCurrent user activity state: "active" or "idle"
screenStatestringCurrent screen state: "locked" or "unlocked"
isSupportedbooleanWhether the native Idle Detection API is supported in the current browser
isPermissionGrantedbooleanWhether permission has been granted for the Idle Detection API
startfunctionFunction to manually start idle detection. Returns a Promise that resolves when detection starts
stopfunctionFunction to manually stop idle detection and reset states

Behavior and lifecycle

start is idempotent until stop runs. Native support uses IdleDetector; otherwise a fallback marks the user idle after no mouse, keyboard, click, scroll, or touch activity and approximates lock state with document visibility. Both paths clamp the threshold to at least 60 seconds. stop resets all public state and fully removes fallback listeners/timers; native change listeners are removed, but the underlying native detector is not aborted by this implementation.

Compatibility and accessibility

isSupported means native Idle Detection support; the fallback can still run when it is false. Native use may require HTTPS, a top-level context, permission, and a user gesture. With requestPermission: false, the hook skips the explicit permission request and start can still fail through onError. Treat idle and visibility as hints, never as proof that a person is absent or a screen is locked, and keep security timeouts independent.

On this page