How to Detect Idle Browser Tabs with the Page Visibility API

William Le

Do you ever wonder if background tabs/windows are using battery power, or precious CPU time? Using the Page Visibility API, we can detect when users aren’t viewing/interacting with our website to help them save precious computing resources! 🔌🔋

The Page Visibility API was introduced in 2011, and includes several features that were added to the Document object:

  • document.hidden: a read-only attribute that returns true when the tab/window is hidden
  • document.visibilityState: a read-only attribute that returns a string value denoting the state of the tab/window: 'hidden' | 'visible' | 'prerender'
  • visibilitychange: an event that’s emitted whenever the tab/window’s visibility changes

The added benefit of using visibilityState over hidden is that you’re able to identify when the browser is in “prerender” phase (eg., when the website is still loading).

Let’s look at some examples that use Page Visibility API!

Practical Example #1

In this example, we pause the video’s playback whenever the tab/window is hidden. This is something that you’ll see many websites do to prevent users from wasting their network bandwidth.

It uses a simple HTML5 <video> embed:

<video id="my-video">
  <source id='mp4' src="cartoon.mp4" type='video/mp4'/>

Now let’s subscribe to the visibilitychange event to automatically pause/play the video:

const onVisibilityChange = () => {
  const video = document.getElementById("my-video");
  return document.hidden
    ? video.pause()

document.addEventListener("visibilitychange", onVisibilityChange);

It’s that simple! For non-mobile users, try out the demo below! Click to play the video, then switch tabs:

See the Pen Page Visibility API Demo by wle8300 (@wle8300) on CodePen.

Practical Example #2

The Page Visibility API isn’t just for optimizing battery/CPU resources (eg., performance optimization). You could use it for ✨ UX enhancement. ✨

As a quick example, if the user is viewing another tab/window you could use the opportunity to download the remainder of the app’s bundle using Webpack’s dynamic import():

let loaded = false;
const onVisibilityChange = () => {
  if (!loaded && document.visibilityState === 'visible') {
      import(/* webpackChunkName: "bundle-[index]"*/ './dist'),
      import(/* webpackPrefetch: 0 */ './images')
    ]).then(() => {
      loaded = true;

document.addEventListener('visibilitychange', onVisibilityChange);

Since the browser will cache these assets, the user will have the entire app loaded once they return to the tab/window. This minimizes any lag that might have occurred as they navigated your app.


A great UX doesn’t necessarily mean building innovative features, or captivating animations. Sometimes showing the user that we’re thinking about them even when they aren’t using our webpage can make more of an impression! 🙈 🐵

I hope you’ll find great ways to use the Page Visibility API to build these optimizing/enhancing strategies!

Visit MDN for documentation on the Page Visibility API

  Tweet It

🕵 Search Results

🔎 Searching...