Library / SDK
serkanyersen/ifvisible.js avatar
serkanyersen/ifvisible.js

ifvisible.js 3.0.0 for Page Visibility and Idle Detection

Crossbrowser & lightweight way to check if user is looking at the page or interacting with it.

1,953 stars183 forksTypeScriptMIT

At a glance

What is it?
ifvisible.js is a TypeScript library for detecting whether a browser page is visible, active, idle, or frozen. It wraps the Page Visibility API and adds interaction-based idle tracking, lifecycle events, interval control, and cleanup for browser applications.
Who is it for?
Adopt ifvisible.js when a browser application needs a shared abstraction for tab visibility, user idleness, page freezing, and timer cleanup. Do not choose it as a replacement for the browser's own Page Visibility API when those states are sufficient.
Can I use it commercially?
Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 133 days ago.
What is it written in?
Mainly TypeScript, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.

Editorial analysis

What ifvisible.js Tracks for Browser Applications

ifvisible.js addresses a specific browser problem: a page can remain open while the user switches tabs, minimizes the browser, stops interacting, or enters the back/forward cache. An application that keeps polling, streaming, animating, or displaying notifications in those situations may waste resources or present stale state. The library gives application code named states and events instead of requiring each feature to interpret `document.visibilityState` and interaction timing independently.

The documented states are `idle`, `hidden`, and `active`. `hidden` represents a page that is not visible, while `active` represents a visible page with active user status. The `idle` state is based on interaction rather than only browser visibility. This distinction matters for a visible browser window left unattended. The library also exposes `blur`, `focus`, `wakeup`, `freeze`, and `resume` events, giving applications separate signals for visibility changes, idle recovery, and page lifecycle changes.

The intended users are developers building browser interfaces that should pause work when a person is not attending to them. Examples in the README include pausing animations, pausing a live-data stream, and stopping automatic updates. The package description identifies the implementation as a wrapper around the HTML5 Page Visibility API, so it is not a general user-attention analytics system and does not establish whether a person is physically looking at the screen.

Page Visibility, Interaction, and Lifecycle Events

The library combines browser visibility state with its own interaction model. `now()` returns the current status, and callers can inspect the result directly or ask whether the page is in a particular state. The README documents `now("hidden")`, which supports conditional logic such as displaying content only when the page is not hidden. Calling `ifvisible.now()` in a non-DOM environment returns `true`, while `ifvisible.isSupported` is `false`, so importing the package during server-side rendering does not require a browser global.

Interaction-related events use a configurable idle duration. The default is 30 seconds, and `setIdleDuration(120)` changes the threshold to 120 seconds. The status methods can also be invoked manually. `idle()` marks the page idle and can accept a callback. `focus()` marks the page active and also fires `wakeup`, while `wakeup()` wakes the page from idle.

Page lifecycle handling is separate from ordinary visibility handling. The `freeze` event indicates that the browser may place a backgrounded page into the back/forward cache, and `resume` indicates that the page has resumed from it. The README advises flushing state or closing connections during `freeze` and treating `resume` as a recovery signal. Those events depend on browser support, and the documentation does not provide a fallback policy for browsers that lack the Page Lifecycle API.

Event handlers can be removed with `off()`. Passing a callback removes that callback, while passing only the event name removes all handlers for that event. Per-instance isolation is a stated v3 feature, so separate `new IfVisible()` instances do not share one global event store.

Installing ifvisible.js and Performing a First Visibility Check

The README gives one npm installation command. It does not document a separate browser bundler setup, so applications should follow their existing bundler conventions after installing the package. The package metadata lists Node >=18 and exposes ESM, CommonJS, and browser global builds.

Install the package with:

bash
npm install ifvisible.js

An ES module application can import the default singleton. The named exports shown in the README are also available:

javascript
import ifvisible, { IfVisible } from "ifvisible.js";

if (ifvisible.now()) {
  openPopUp();
}

A CommonJS application uses the named export:

javascript
const { ifvisible } = require("ifvisible.js");

For a script-tag integration, load the global build and call the singleton exposed by the browser build:

html
<script src="https://unpkg.com/ifvisible.js"></script>
<script>
  ifvisible.now();
</script>

The package metadata identifies the unpkg file as `dist/ifvisible.global.js`. A bundler-based application should prefer the package's import or require entry points so that module resolution and type declarations are handled by the toolchain.

Pausing Work with idle, blur, freeze, and onEvery

ifvisible.js is useful when a recurring browser task should stop while the page is unattended. The README shows `onEvery(0.5, callback)` for work that should run every half second while the page is visible. The library pauses the interval when the user is idle or not looking at the page, then resumes it when the user returns. The callback in the example animates a logo, but the same mechanism could be applied to a dashboard refresh or other UI task.

For visibility changes, application code can pause animations on `blur` and resume them on `focus`:

javascript
ifvisible.on("blur", function () {
  animations.pause();
});

ifvisible.on("focus", function () {
  animations.resume();
});

Idle handling is separate. An application can pause a live stream on `idle` and resume it on `wakeup`. The library does not define what the application should do while it is frozen, so a `freeze` handler must explicitly flush state or close connections if that behavior is needed.

The feature is a scheduling aid, not a general cancellation system. The documentation does not claim that an arbitrary promise, network request, animation library, or websocket is automatically stopped. The application still owns the cleanup logic for those resources.

Instance Cleanup and Package Delivery Boundaries

The v3 package includes bundled TypeScript declarations and separate distribution formats. `package.json` points `main` to `./dist/ifvisible.cjs`, `module` to `./dist/ifvisible.js`, `types` to `./dist/ifvisible.d.ts`, and browser CDN fields to `./dist/ifvisible.global.js`. The package publishes only the `dist` directory according to its `files` field. This gives consumers a defined package surface, but it also means source files and repository tooling are not part of the published package.

For single-page applications, `destroy()` is an important lifecycle operation. The README states that it removes every DOM listener, handler, and timer. A component that creates an instance and later unmounts can follow this pattern:

javascript
const instance = new IfVisible();

// ...later
instance.destroy();

The library is side-effect free at the package level according to `sideEffects: false`. Its import-time behavior is also documented as SSR-safe, with no DOM access during import. That protects server rendering from an immediate browser-only exception, but it does not mean a non-DOM environment can report meaningful browser visibility. In that case, `isSupported` is `false` and the methods are safe no-ops, with `now()` returning `true`.

These boundaries are worth checking in an application that creates instances dynamically. The README documents cleanup, but it does not describe automatic cleanup for framework component unmounting, so the owning code must call `destroy()` at the correct point.

Limitations and a Direct Alternative

The main limitation is that ifvisible.js depends on browser-provided visibility and lifecycle signals. The package metadata requires Node >=18 for installation and development tooling, but Node does not provide the browser states that the library is designed to observe. Server-side rendering is safe to import, not capable of determining whether a browser user is present.

A direct alternative is to use the browser's Page Visibility API directly. Code can read `document.visibilityState` and listen for the `visibilitychange` event without adding an idle timer, interaction detection, lifecycle event abstraction, or `onEvery()` scheduler. That approach is simpler when an application only needs to distinguish visible and hidden tabs. ifvisible.js adds value when several features need shared `active`, `idle`, `blur`, `focus`, `wakeup`, `freeze`, and `resume` handling.

Another practical limitation is browser feature coverage. The README specifically describes the Page Lifecycle API for `freeze` and `resume`, but it does not document a polyfill or define behavior for browsers without that API. The library also cannot guarantee that a user's physical gaze matches page visibility. A visible page can remain idle, and a user may be looking at another display or application.

The project should be treated as a browser UI utility, not as evidence of engagement or a complete background task scheduler.

Maintenance Signals, License, and Upgrade Considerations

The repository is not archived, and its last push was on 2026-06-12. The repository contains TypeScript source, build configuration, tests, documentation, and package metadata. The package version is `3.0.0`, while the repository release list includes older entries through `v2.0.10 (RC)` dated 2017-03-05. That difference makes the release history worth checking before selecting a version, especially for projects migrating from an earlier API.

The README identifies v3 as a clean-break rewrite. It removes legacy Internet Explorer and vendor-prefix code, uses the standard Page Visibility API, changes packaging to modern ESM, CommonJS, and IIFE builds, adds bundled `.d.ts` files, introduces per-instance event isolation, adds `freeze` and `resume`, and adds `destroy()`. Projects upgrading from v2 should therefore expect code and integration changes rather than a drop-in compatibility path.

The package is licensed under MIT. That permits use and modification under the license terms, but this review does not interpret those terms as legal advice. Consumers should copy and review the `LICENSE` file for their own compliance and distribution process. The package metadata also declares the package as side-effect free, which can assist bundler tree-shaking, although application behavior still depends on how events and instances are used.

The last push date is a maintenance signal, not a guarantee of browser compatibility or release cadence.

Editorial conclusion

Adopt ifvisible.js when a browser application needs a shared abstraction for tab visibility, user idleness, page freezing, and timer cleanup. Do not choose it as a replacement for the browser's own Page Visibility API when those states are sufficient. Before adoption, verify the required event support, teardown path, and bundler behavior in the browsers used by the application. The package is published under the MIT license, with package metadata requiring Node >=18.

Frequently asked questions

How do I install ifvisible.js?

The README documents `npm install ifvisible.js`. The package metadata specifies Node >=18 and exposes ESM, CommonJS, and browser global builds.

Can ifvisible.js detect when the user is idle?

Yes. The README documents `idle` and `wakeup` events and a default idle duration of 30 seconds. `setIdleDuration(120)` changes the duration to 120 seconds.

Does ifvisible.js work during server-side rendering?

The README describes importing it as SSR-safe. In a non-DOM environment, `ifvisible.isSupported` is `false`, methods are safe no-ops, and `now()` returns `true`.

How do I clean up ifvisible.js in a single-page application?

Call `destroy()` on the instance when its owner is torn down. The README says this detaches every DOM listener, handler, and timer.

Official sources

  1. License: MIT
  2. Project website
  3. README
  4. Releases
  5. serkanyersen/ifvisible.js on GitHub
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/serkanyersen-ifvisible-js.svg)](https://hysenlabs.com/projects/serkanyersen-ifvisible-js)

Was this useful?