useWakeLock
A specialized device hook that leverages the Screen Wake Lock API to prevent the device screen from dimming or turning off during usage.
Installation
npx shadcn@latest add https://r.fiberui.com/r/hooks/use-wake-lock.jsonA React hook that provides access to the Screen Wake Lock API, allowing you to prevent the device screen from dimming or locking. Perfect for recipe apps, presentation tools, or map navigation.
Browser Support
Checks for isSupported. Note that the lock is automatically released if
the page loses visibility (e.g., user switches tabs or minimizes window).
Features
- Prevent Sleep - Keep screen active
- Auto Release - Detects when system releases lock (visibility change)
- Error Handling - Handles permission denials or battery saver mode restrictions
- Type Safe - Fully typed for TypeScript
- SSR Safe - Works safely with server-side rendering
Basic Usage
A toggle to keep the screen awake.
Screen Wake Lock: Inactive
Your screen will sleep according to system settings.
Wake Lock API is not supported in this browser.
"use client";
import { useWakeLock } from "@repo/hooks/device/use-wake-lock";
import { Button } from "@repo/ui/components/button";
import { Lock, Unlock, Zap } from "lucide-react";
export function Example1() {
const { isActive, isSupported, request, release, error } = useWakeLock();
const handleToggle = async () => {
if (isActive) {
await release();
} else {
await request();
}
};
return (
<div className="flex flex-col gap-4">
<div
className={`flex flex-col items-center justify-center gap-2 rounded-xl border p-6 transition-colors ${
isActive
? "border-green-500/50 bg-green-500/10 text-green-600 dark:text-green-400"
: "border-zinc-200 bg-zinc-100 dark:border-zinc-700 dark:bg-zinc-800/50"
}`}
>
<div
className={`rounded-full p-3 ${isActive ? "bg-green-500/20" : "bg-zinc-200 dark:bg-zinc-700"}`}
>
<Zap
className={`h-6 w-6 ${isActive ? "fill-current" : "opacity-50"}`}
/>
</div>
<div className="text-lg font-bold">
Screen Wake Lock: {isActive ? "Active" : "Inactive"}
</div>
<div className="max-w-xs text-center text-sm opacity-80">
{isActive
? "Your screen will prevent sleep efficiently."
: "Your screen will sleep according to system settings."}
</div>
</div>
{error && (
<div className="text-destructive bg-destructive/10 rounded p-2 text-sm">
{error.message}
</div>
)}
{!isSupported && (
<div className="rounded bg-yellow-100 p-2 text-sm text-yellow-600 dark:bg-yellow-900/30 dark:text-yellow-400">
Wake Lock API is not supported in this browser.
</div>
)}
<Button
onClick={handleToggle}
isDisabled={!isSupported}
size="lg"
className="w-full self-center sm:w-auto"
>
{isActive ? (
<>
<Unlock className="mr-2 h-4 w-4" />
Release Lock
</>
) : (
<>
<Lock className="mr-2 h-4 w-4" />
Acquire Lock
</>
)}
</Button>
</div>
);
}
API Reference
Hook Signature
function useWakeLock(): UseWakeLockReturn;Return Value
| Property | Type | Description |
|---|---|---|
isActive | boolean | true if verify the wake lock is active |
isSupported | boolean | true if Screen Wake Lock API is supported |
request | () => Promise<boolean> | Requests the wake lock. Returns true if granted |
release | () => Promise<boolean> | Manualy releases the wake lock |
error | Error | null | Error object if request fails |
Hook Source Code
import { useState, useCallback, useEffect, useRef } from "react";
/**
* Return type for the useWakeLock hook
*/
export interface UseWakeLockReturn {
/** Whether the wake lock is currently active */
isActive: boolean;
/** Whether the Screen Wake Lock API is supported */
isSupported: boolean;
/** Request a wake lock */
request: () => Promise<boolean>;
/** Release the wake lock */
release: () => Promise<boolean>;
/** Error from the last operation */
error: Error | null;
}
// Type definitions for Screen Wake Lock API
// These are often already in lib.dom.d.ts but strictly typing local interfaces helps if libs are old
interface WakeLockSentinel extends EventTarget {
readonly released: boolean;
readonly type: "screen";
release(): Promise<void>;
onrelease: ((this: WakeLockSentinel, ev: Event) => void) | null;
}
interface WakeLock {
request(type: "screen"): Promise<WakeLockSentinel>;
}
// Extend Navigator if not already defined correctly in environment
// We use a safe augmentation or just cast usage
declare global {
interface Navigator {
// Only add if not present, but TS merging rules make this hard if it conflicts.
// Best to just rely on usage casting if conflict exists.
// wakeLock?: WakeLock;
}
}
/**
* A React hook that provides access to the Screen Wake Lock API,
* allowing you to prevent the device screen from dimming or locking.
*
* @returns UseWakeLockReturn object with state and controls
*/
export function useWakeLock(): UseWakeLockReturn {
const [isActive, setIsActive] = useState(false);
const [error, setError] = useState<Error | null>(null);
const sentinelRef = useRef<WakeLockSentinel | null>(null);
// Check support
const isSupported =
typeof navigator !== "undefined" && "wakeLock" in navigator;
// Release function
const release = useCallback(async () => {
if (!sentinelRef.current) return false;
try {
await sentinelRef.current.release();
sentinelRef.current = null;
setIsActive(false);
return true;
} catch (err) {
const error =
err instanceof Error
? err
: new Error("Failed to release lock");
setError(error);
return false;
}
}, []);
// Request function
const request = useCallback(async () => {
if (!isSupported) {
setError(new Error("Screen Wake Lock API is not supported"));
return false;
}
try {
const sentinel = await navigator.wakeLock!.request("screen");
sentinelRef.current = sentinel;
setIsActive(true);
setError(null);
// Handle automatic release (e.g. user switches tabs)
sentinel.onrelease = () => {
// If it was released externally (not by us calling release())
// we should update state.
if (sentinelRef.current === sentinel) {
// Released by system
setIsActive(false);
sentinelRef.current = null;
}
};
return true;
} catch (err) {
const error =
err instanceof Error
? err
: new Error("Failed to request lock");
setError(error);
return false;
}
}, [isSupported]);
// Cleanup on unmount
useEffect(() => {
return () => {
if (sentinelRef.current) {
sentinelRef.current.release().catch(() => {});
}
};
}, []);
return {
isActive,
isSupported,
request,
release,
error,
};
}
export default useWakeLock;