· deepdives  · 7 min read

Mastering User Experience: A Deep Dive into the Screen Wake Lock API

Learn how to use the Screen Wake Lock API to prevent device screens from sleeping during critical tasks. This guide covers browser support, practical code examples (vanilla JS, TypeScript, React hook), fallbacks, UX considerations, performance tips, and real-world use cases.

Learn how to use the Screen Wake Lock API to prevent device screens from sleeping during critical tasks. This guide covers browser support, practical code examples (vanilla JS, TypeScript, React hook), fallbacks, UX considerations, performance tips, and real-world use cases.

Why preventing screen sleep matters

Screens that unexpectedly dim or sleep during critical tasks - navigation, video calls, cooking timers, guided workouts, or point-of-sale kiosks - break flow and harm user experience. Traditionally web apps had few safe, standardized ways to keep the device awake. The Screen Wake Lock API changes that by offering a clean API to request that the screen remain on while your app needs it.

This article explains how the API works, best practices for a responsible implementation, fallback strategies for unsupported browsers, practical code examples (vanilla JS, TypeScript, and a React hook), and real-world scenarios where a wake lock improves UX.

What the Screen Wake Lock API does (and doesn’t)

  • The API allows a web page to request a wake lock of type screen, which prevents the display from dimming or locking while the document is visible.
  • It is intended for short-lived, user-initiated use - not indefinite background persistence.
  • The API does not override system-level power policies or permissions on the device; the user agent (browser/OS) may still enforce limits.

Useful references:

Browser support and feature detection

Support has improved but is not universal. Always do feature detection and provide a fallback.

Basic detection:

if ('wakeLock' in navigator) {
  // Safe to attempt a wake lock
} else {
  // Provide fallback or graceful degradation
}

Caniuse and MDN are good places to check current support and platform caveats. If support is missing, consider a graceful fallback (see the fallback section below).

Core API patterns

At its simplest, requesting a screen wake lock looks like this:

let wakeLock = null;

async function requestWakeLock() {
  try {
    wakeLock = await navigator.wakeLock.request('screen');
    console.log('Wake lock active');

    // Optional: listen for release events
    wakeLock.addEventListener('release', () => {
      console.log('Wake lock released');
    });
  } catch (err) {
    console.error(`Wake lock request failed: ${err.name}, ${err.message}`);
  }
}

async function releaseWakeLock() {
  if (wakeLock) {
    try {
      await wakeLock.release();
      wakeLock = null;
    } catch (err) {
      console.error('Failed to release wake lock:', err);
    }
  }
}

Important notes:

  • The returned object is a WakeLockSentinel. You can call .release() or listen for its release event.
  • The sentinel can be invalidated by the UA (browser) for reasons such as power saving.
  • Re-requesting may be necessary when the document regains visibility.

Handling visibility changes and re-acquisition

A common pattern is to re-request the wake lock when the page becomes visible again. Browsers may automatically release wake locks when a tab is hidden or when the OS applies power-saving policies.

document.addEventListener('visibilitychange', async () => {
  if (document.visibilityState === 'visible') {
    await requestWakeLock();
  } else {
    await releaseWakeLock();
  }
});

This ensures the lock is active only when the user can see and interact with the page.

Using AbortController to manage lifetime

Some implementations accept an AbortSignal so you can abort the request cleanly.

const ac = new AbortController();

try {
  wakeLock = await navigator.wakeLock.request('screen', { signal: ac.signal });
} catch (err) {
  if (err.name === 'AbortError') {
    console.log('Wake lock request aborted');
  }
}

// Later to cancel
ac.abort();

Check support for the options object in your target browsers.

Practical implementation: TypeScript example

let wakeLock: WakeLockSentinel | null = null;

export async function enableWakeLock(): Promise<void> {
  if (!('wakeLock' in navigator)) return;
  try {
    wakeLock = await (navigator as any).wakeLock.request('screen');
    wakeLock.addEventListener('release', () =>
      console.log('Wake lock released')
    );
  } catch (err) {
    console.error('Could not get wake lock:', err);
  }
}

export async function disableWakeLock(): Promise<void> {
  if (!wakeLock) return;
  try {
    await wakeLock.release();
  } finally {
    wakeLock = null;
  }
}

(You may need to augment global declarations for WakeLockSentinel or use any depending on your TypeScript config.)

React hook: useWakeLock

A reusable React hook encapsulates the logic and visibility handling:

import { useEffect, useRef } from 'react';

export function useWakeLock(enabled) {
  const wakeLockRef = useRef(null);

  useEffect(() => {
    if (!('wakeLock' in navigator) || !enabled) return;

    let mounted = true;

    async function requestLock() {
      try {
        wakeLockRef.current = await navigator.wakeLock.request('screen');
        wakeLockRef.current.addEventListener('release', () =>
          console.log('released')
        );
      } catch (err) {
        console.error('Wake lock failed', err);
      }
    }

    requestLock();

    function handleVisibility() {
      if (document.visibilityState === 'visible' && enabled) requestLock();
    }

    document.addEventListener('visibilitychange', handleVisibility);

    return () => {
      mounted = false;
      document.removeEventListener('visibilitychange', handleVisibility);
      if (wakeLockRef.current) wakeLockRef.current.release();
    };
  }, [enabled]);
}

Use the hook in a component that has a user-controlled toggle for wake-lock behavior.

Fallback strategies for unsupported environments

  • NoSleep.js: A widely used workaround that plays an invisible looping video to keep the device awake. Not ideal (hacks, power/performance issues), but effective on older browsers: https://github.com/richtr/NoSleep.js
  • In many cases, educating the user and offering an explanatory toggle (“Keep screen on”) that suggests they change native device settings can be acceptable.
  • Progressive enhancement: only enable wake lock features when supported, and ensure your app behaves acceptably without it.

UX and ethical considerations (don’t be reckless)

  • Request wake locks only for clearly user-initiated and necessary tasks (guided workouts, timers, navigation, POS, healthcare monitoring).
  • Always show a visible indicator (icon or status line) when a wake lock is active so users know their screen will stay on.
  • Provide an easy way to disable the behavior (toggle in UI, automatic release after task completion).
  • Be transparent about battery impact, especially for long-running operations.
  • Test on battery-limited devices and explain to users when keeping the screen on may drain battery.

Real-world use cases and patterns

  • Navigation and maps: Keep the screen on during turn-by-turn navigation so users don’t have to touch the device while driving or walking.
  • Video and conferencing apps: Prevent the screen from dimming during active calls or when streaming live video that doesn’t rely on the
  • Workout/fitness timers: Keep the display on for exercise sets, timers, or guided workout steps.
  • Kiosk/interactive displays: Maintain a consistent experience for customers interacting with a public display.
  • Medical/monitoring dashboards: Ensure critical monitoring dashboards stay visible during patient observations.
  • Presentations and demos: Prevent displays from sleeping mid-presentation.

Testing checklist

  • Test on iOS, Android, desktop browsers - behavior and support differ.
  • Test with battery saver modes enabled; some UAs may force release.
  • Verify visibility-change handling by switching tabs, locking/unlocking the device, or moving to a different app.
  • Ensure wake locks are released on unload and on component unmount.
  • Check for memory leaks: if you add event listeners on the sentinel, remove them when releasing.

Performance and resource advice

  • Wake locks prevent the screen from sleeping, which increases battery usage. Use them sparingly.
  • Avoid combining unnecessary timers, heavy animations, and a wake lock; each increases energy consumption.
  • Release the lock as soon as the task completes. Do not hold the lock indefinitely.

Debugging common issues

  • Request fails with NotAllowedError or SecurityError: ensure the page is served in a secure context (HTTPS) and that your UA supports wake lock in the current state. Some platforms require a user gesture.
  • Wake lock is released unexpectedly: handle release events and re-acquire on visibilitychange when appropriate.
  • Wake lock seems ignored on mobile: many mobile OSs aggressively suspend webpages; check your target browser’s policies.

Example: Minimal production-ready pattern

A robust pattern ties together feature detection, re-acquisition on visibility changes, and a UI indicator:

let sentinel = null;

async function enableKeepAwake() {
  if (!('wakeLock' in navigator)) return false;
  try {
    sentinel = await navigator.wakeLock.request('screen');
    sentinel.addEventListener('release', () => updateUI(false));
    updateUI(true);
    return true;
  } catch (err) {
    console.warn('Wake lock unavailable:', err);
    updateUI(false);
    return false;
  }
}

async function disableKeepAwake() {
  if (!sentinel) return;
  await sentinel.release();
  sentinel = null;
  updateUI(false);
}

document.addEventListener('visibilitychange', async () => {
  if (document.visibilityState === 'visible' && sentinel == null) {
    await enableKeepAwake();
  }
});

function updateUI(active) {
  const el = document.getElementById('wakeLockIndicator');
  if (!el) return;
  el.textContent = active ? 'Keep screen on - active' : 'Keep screen on - off';
}

Final checklist before shipping

  • Add feature detection and graceful fallback.
  • Only enable on explicit user action or clearly user-expected flows.
  • Provide a visible status and easy way to disable.
  • Handle visibility changes and sentinel release events.
  • Test across devices and battery saver scenarios.
  • Monitor analytics (how often feature is used) and user feedback for battery complaints.

Further reading

By using the Screen Wake Lock API responsibly - with proper feature detection, user consent, visible UI affordances, and fallbacks - you can significantly improve user experience for tasks where screen-on behavior is essential, while minimizing unwanted battery drain and respecting platform constraints.

Back to Blog

Related Posts

View All Posts »
Understanding the DOM Scheduling API: Revolutionizing UI Performance

Understanding the DOM Scheduling API: Revolutionizing UI Performance

Learn how the DOM Scheduling (Task Scheduling) API changes the way we schedule main-thread work. This tutorial compares the Scheduling API with requestAnimationFrame, requestIdleCallback and setTimeout, includes practical code examples, benchmarks and fallbacks, and shows how to make UIs feel snappier.