Skip to content

Instantly share code, notes, and snippets.

@Wigny
Created October 13, 2025 19:09
Show Gist options
  • Select an option

  • Save Wigny/b74595a7a4e37e0ece97558e81a41682 to your computer and use it in GitHub Desktop.

Select an option

Save Wigny/b74595a7a4e37e0ece97558e81a41682 to your computer and use it in GitHub Desktop.
import { useEffect, useState } from 'react';
import { type Duration } from 'dayjs/plugin/duration';
import { throttle } from 'lodash-es';
const events: Array<keyof DocumentEventMap> = [
'mousemove',
'keydown',
'wheel',
'mousedown',
'touchstart',
'touchmove',
'focus',
'timeupdate',
];
/**
* Detects user inactivity after a specified timeout period.
*
* Returns `true` when the user has been idle for longer than the specified timeout.
*/
export function useIdle(timeout: Duration): boolean {
const [idle, setIdle] = useState(false);
const ms = timeout.asMilliseconds();
useEffect(() => {
let timeoutId: number;
const setIdleTimer = () => {
setIdle(false);
window.clearTimeout(timeoutId);
timeoutId = window.setTimeout(() => setIdle(true), ms);
};
const handleEvent = throttle(setIdleTimer, 500, { leading: true, trailing: false });
const handleVisibilityChange = () => {
if (!document.hidden) handleEvent();
};
events.forEach(event => document.addEventListener(event, handleEvent, { capture: true, passive: true }));
document.addEventListener('visibilitychange', handleVisibilityChange);
setIdleTimer();
return () => {
events.forEach(event => document.removeEventListener(event, handleEvent, { capture: true }));
document.removeEventListener('visibilitychange', handleVisibilityChange);
window.clearTimeout(timeoutId);
};
}, [ms]);
return idle;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment