
ifvisible.js is a lightweight JavaScript library that detects page visibility and user idle state.
It wraps the standard Page Visibility API and monitors mouse, keyboard, and touch events to determine when your user becomes inactive or returns to the page.
You can use the library to pause animations, delay live data updates, stop background polling, show session warnings, and resume frontend work when the user returns.
Features:
- Detects page visibility using the standard Page Visibility API.
- Tracks user idle state via mouse, keyboard, touch, and scroll events.
- Fires blur, focus, idle, wakeup, freeze, and resume events.
- Provides a method for smart intervals that pause when idle or hidden.
- Supports configurable idle duration in seconds.
- Offers per-instance isolation with multiple independent instances.
- SSR-safe with no-op fallback when the DOM is unavailable.
How To Use It:
Installation
Install the package from npm.
npm install ifvisible.js
Import the default singleton in an ES module project.
import ifvisible from "ifvisible.js";
Named exports are also available when you need a separate instance or TypeScript event types.
import { ifvisible, IfVisible, type IfVisibleEvent } from "ifvisible.js";
CommonJS projects can load the package with require.
const { ifvisible } = require("ifvisible.js");
Browser-only pages can use the global build from a CDN.
<script src="https://unpkg.com/ifvisible.js"></script> <script> console.log(ifvisible.now()); </script>
Basic Usage
Import the singleton and call now() to check the current page state synchronously.Set the idle duration in seconds before you start relying on idle behavior.
import ifvisible from "ifvisible.js";
// Mark the user as idle after 45 seconds of inactivity.
ifvisible.setIdleDuration(45);
// Check whether the page is active right now.
if (ifvisible.now()) {
refreshProjectStats();
}
// Pause work after the user becomes idle.
ifvisible.on("idle", function () {
pauseActivityFeed();
});
// Resume work after the user returns.
ifvisible.on("wakeup", function () {
resumeActivityFeed();
});
Advanced Usages:
Pausing and Resuming on Tab Switch
Tab-switching events need separate handlers for when the user leaves and returns. The blur event fires when the page becomes hidden, and focus fires when it returns to the foreground.
ifvisible.on("blur", function () {
audioPlayer.pause(); // stop audio when tab is hidden
});
ifvisible.on("focus", function () {
audioPlayer.resume(); // restart when tab regains focus
});
Responding to Idle and Wakeup
Live data streams waste resources when the user has stepped away. This pattern pauses a feed when idle fires and restarts it when the user comes back.
ifvisible.on("idle", function () {
marketFeed.pause(); // stop polling quotes
});
ifvisible.on("wakeup", function () {
marketFeed.resume(); // restart on user return
});
Setting a Custom Idle Duration
The default idle timeout is 30 seconds. Pass a value in seconds to setIdleDuration to change it at any point after initialization.
ifvisible.setIdleDuration(300); // treat user as idle after 5 minutes
Smart Intervals
onEvery runs a callback on a repeating interval. The interval pauses automatically when the page enters an idle or hidden state and restarts when active resumes.
// Rotate a notification badge only while the tab is active
ifvisible.onEvery(2, function () {
updateBadgeCount();
});
Checking Status Directly
Passing a status string to now() returns a boolean for that specific state. This works well for one-off conditional checks at the call site. No event listener setup needed.
if (ifvisible.now("hidden")) {
skipAssetPreload(); // avoid loading resources in a background tab
}
if (ifvisible.now("idle")) {
triggerAutoSave(); // save form progress while user is away
}
// Possible values: "active", "idle", "hidden"
Reading Idle Info
getIdleInfo() returns a snapshot of the current idle state. The returned object covers the idle status, how long since the last interaction, remaining time before idle fires, and percentage progress toward the threshold.
const info = ifvisible.getIdleInfo(); // info.isIdle - true if the page is currently idle // info.idleFor - milliseconds since last user interaction // info.timeLeft - milliseconds remaining until idle fires // info.timeLeftPer - percentage progress toward the idle timeout console.log(info);
Page Lifecycle Events
Modern browsers freeze backgrounded tabs for bfcache and restore them later. These events let you flush state before freeze and reinitialize connections after resume.
ifvisible.on("freeze", function () {
pendingQueue.flush(); // persist data before the tab freezes
});
ifvisible.on("resume", function () {
wsConnection.reconnect(); // restore connections after bfcache restore
});
Per-Instance Teardown in SPAs
Create a named instance per component or route. Call destroy() on unmount to remove all listeners and timers attached to that instance.
import { IfVisible } from "ifvisible.js";
const tracker = new IfVisible();
tracker.on("idle", () => {
recordIdleSession(currentUserId);
});
// On component unmount or route change:
tracker.destroy();
Configuration Options:
All configuration goes through method calls.
setIdleDuration(seconds)(number): Sets how many seconds of inactivity trigger the idle state. Default is 30.getIdleDuration()(number): Returns the current idle timeout value in milliseconds.getIdleInfo()(object): Returns{ isIdle, idleFor, timeLeft, timeLeftPer }.
API Methods:
// Returns true when the page status matches the given string.
// With no argument, returns true only when status is "active".
ifvisible.now(); // true if active
ifvisible.now("idle"); // true if idle
ifvisible.now("hidden"); // true if hidden
// Attaches a listener for the named event.
ifvisible.on("idle", callback);
// Removes a specific listener or clears all handlers for the event.
ifvisible.off("idle", callback); // removes one handler
ifvisible.off("idle"); // removes all idle handlers
// Sets the idle timeout in seconds.
ifvisible.setIdleDuration(120);
// Returns the current idle timeout in milliseconds.
ifvisible.getIdleDuration();
// Returns a snapshot of the current idle state.
ifvisible.getIdleInfo();
// Runs a callback every N seconds while the page is active.
// Pauses automatically when idle or hidden.
ifvisible.onEvery(3, callback);
// Manually forces a status transition.
ifvisible.idle(); // forces idle state
ifvisible.blur(); // forces hidden state
ifvisible.focus(); // forces active state and fires wakeup
ifvisible.wakeup(); // wakes from idle; no focus event needed
// Removes all DOM listeners, event handlers, and timers.
ifvisible.destroy();
Events:
// Fires when the page loses visibility (tab switch or window minimize).
ifvisible.on("blur", function () {});
// Fires when the page regains visibility.
ifvisible.on("focus", function () {});
// Fires when the user has been inactive for the configured idle duration.
ifvisible.on("idle", function () {});
// Fires when the user interacts with the page after an idle period.
ifvisible.on("wakeup", function () {});
// Fires when the browser freezes the tab for bfcache.
ifvisible.on("freeze", function () {});
// Fires when the browser restores a frozen tab.
ifvisible.on("resume", function () {});
// Fires on any status transition. Contains a status field.
ifvisible.on("statusChanged", function (event) {
console.log(event.status); // "active" | "idle" | "hidden"
});
Alternatives:
- Intelligent Idle Detection With The Idleness JavaScript Library
- Trigger A Function When The User Goes Idle – idle-tracker
- Lightweight User Idle Tracker In Vanilla JavaScript
- Detect User Activity And Fire Callback After A Idle Time – idle.js
FAQs:
Q: Can I use ifvisible.js in a React or Vue component?
A: Yes. Import the IfVisible class, create an instance in your component setup, attach listeners in a lifecycle hook (useEffect or onMounted), and call destroy() in the cleanup function to prevent listener leaks on unmount.
Q: Does ifvisible.js work in server-side rendering frameworks like Next.js or Nuxt?
A: Yes. The library is SSR-safe.
Q: Why does now() return true during server-side rendering?
A: In a non-DOM environment, the library runs as a safe no-op. This prevents import-time crashes during SSR before the browser takes over.
Q: The idle event is not firing. What should I check?
A: Call getIdleDuration() to confirm the current timeout value and getIdleInfo().idleFor to see how long since the last interaction. Confirm that now() returns true on page load. Idle detection only starts after the library initializes in the browser.
Q: How do I remove a specific event listener added with on()?
A: Store the callback in a variable and pass it to off() with the event name: ifvisible.off("idle", myCallback). Calling off() with only the event name removes every handler registered for that event.
Q: What is the difference between blur and idle?
A: The blur event fires immediately when the page loses visibility, such as when the user switches tabs or minimizes the browser window. The idle event fires only after the configured period of inactivity passes with no mouse, keyboard, touch, or scroll input, regardless of tab visibility.





