Skip to content

Idle Detection API

Published

Idle Detection API — the IdleDetector interface lets a page ask whether the user has interacted with the device within a given threshold, and whether the screen is locked, firing a change event whenever either state flips.

  • Legend
  • Yes
  • Partial
  • Flag
  • No
  • Unknown
Browser / PlatformSupportVersionsConfidenceSourceNotes
Chrome (Desktop)Yes94highsource—
Chrome (Android)Yes94highsource1
Edge (Desktop)Yes114highsource—
Firefox (Desktop)No—highsource2
Firefox (Android)No—highsource34
Safari (macOS)No—highsource5
Safari (iOS)No—highsource67
Samsung InternetYes17.0highsource8
WebView (Android)Yes94highsource9
  1. Derived by browser-compat-data mirroring from Chrome.
  2. No Firefox support is recorded in browser-compat-data.
  3. No Firefox for Android support is recorded in browser-compat-data.
  4. Derived by browser-compat-data mirroring from Firefox.
  5. No Safari support is recorded in browser-compat-data.
  6. No Safari on iOS support is recorded in browser-compat-data.
  7. Derived by browser-compat-data mirroring from Safari.
  8. Derived by browser-compat-data mirroring from Chrome Android.
  9. Derived by browser-compat-data mirroring from Chrome Android.

Source data: /compatibility/idle-detection.json · Global usage: 72 % (StatCounter 2026-05)

Source: spec · MDN · Last verified 2026-10-03 · Confidence: high (computed from sources)

The specification says start() fails unless the idle-detection permission has been granted, and IdleDetector.requestPermission() rejects without transient user activation — so it belongs inside a user-gesture handler. start() takes a threshold in milliseconds:

// Minimal, self-contained: create the control the handler needs, then wire it up.
const startButton = document.createElement('button');
startButton.type = 'button';
startButton.textContent = 'Watch idle state';
document.body.append(startButton);
startButton.addEventListener('click', async () => {
const permission = await IdleDetector.requestPermission();
if (permission !== 'granted') return;
const detector = new IdleDetector();
detector.addEventListener('change', () => {
console.log(`User: ${detector.userState}, Screen: ${detector.screenState}`);
});
await detector.start({ threshold: 60_000 });
});

userState reports "active" or "idle"; screenState reports "locked" or "unlocked".

Check for IdleDetector on window before referencing the class, and take an explicit fallback branch when it is absent:

async function watchIdleState(onChange) {
if (!('IdleDetector' in window)) {
// IdleDetector is unavailable, so report unknown and leave the feature's UI hidden.
onChange({ userState: 'unknown', screenState: 'unknown' });
return;
}
const permission = await IdleDetector.requestPermission();
if (permission !== 'granted') {
onChange({ userState: 'unknown', screenState: 'unknown' });
return;
}
const detector = new IdleDetector();
detector.addEventListener('change', () => {
onChange({ userState: detector.userState, screenState: detector.screenState });
});
await detector.start({ threshold: 60_000 });
}
  • Per MDN, the API is available only in secure contexts, so an insecure origin does not expose IdleDetector at all.
  • Call IdleDetector.requestPermission() from inside a user gesture — per MDN it requires transient user activation.
  • Per the WICG specification, start() rejects with a TypeError when threshold is below 60,000 ms; there is no way to ask for finer-grained reporting.
  • The WICG specification initializes userState and screenState to null, so do not read them right after constructing the detector.
  • Per MDN’s browser-compat-data, Firefox and Safari do not implement IdleDetector, so those browsers take the fallback branch above.

For the full reference (permission model, change event, threshold rules), see Idle Detection API.

See also Screen Wake Lock for the inverse problem — keeping the screen awake rather than observing when it locks.

← Back to the Compatibility explorer.