useFullscreen
EditControls standards-based fullscreen mode for an element or the page.
import { useCallback, useEffect, useLayoutEffect, useRef, useState, useSyncExternalStore,} from "react";
import type { RefObject } from "react";
export interface UseFullscreenReturn { enter: () => Promise<boolean>; error: Error | null; exit: () => Promise<boolean>; isFullscreen: boolean; isSupported: boolean; toggle: () => Promise<boolean>;}
const subscribeToFullscreen = (notify: () => void): (() => void) => { if (typeof document === "undefined") return () => {}; document.addEventListener("fullscreenchange", notify); return () => document.removeEventListener("fullscreenchange", notify);};
const getFullscreenElement = (): Element | null => typeof document === "undefined" ? null : document.fullscreenElement;
const useSafeLayoutEffect = typeof window === "undefined" ? useEffect : useLayoutEffect;
/** Controls standards-based fullscreen mode for a ref or the document root. */export const useFullscreen = <ElementType extends Element = HTMLElement>( ref?: RefObject<ElementType | null>,): UseFullscreenReturn => { const fullscreenElement = useSyncExternalStore( subscribeToFullscreen, getFullscreenElement, () => null, ); const [error, setError] = useState<Error | null>(null); const targetRef = useRef<Element | null>(null); const mountedRef = useRef(false); const fullscreenRef = useRef(false); const target = typeof document === "undefined" ? null : ref === undefined ? document.documentElement : ref.current; const isFullscreen = fullscreenElement !== null && fullscreenElement === target;
useSafeLayoutEffect(() => { targetRef.current = typeof document === "undefined" ? null : ref === undefined ? document.documentElement : ref.current; fullscreenRef.current = fullscreenElement !== null && fullscreenElement === targetRef.current; }); useSafeLayoutEffect(() => { mountedRef.current = true; return () => { mountedRef.current = false; }; }, []); const isSupported = typeof document !== "undefined" && typeof document.documentElement.requestFullscreen === "function" && typeof document.exitFullscreen === "function";
useEffect(() => { if (typeof document === "undefined") return; const onError = (event: Event) => { const eventTarget = event.target; if ( typeof Element === "function" && eventTarget instanceof Element && eventTarget !== targetRef.current ) { return; } if (mountedRef.current) { setError(new Error("The fullscreen request failed")); } }; document.addEventListener("fullscreenerror", onError, true); return () => document.removeEventListener("fullscreenerror", onError, true); }, []);
const enter = useCallback(async (): Promise<boolean> => { const element = targetRef.current; if ( element === null || typeof element.requestFullscreen !== "function" || typeof document.exitFullscreen !== "function" ) { return false; } try { if (mountedRef.current) setError(null); await element.requestFullscreen(); return true; } catch (cause) { if (mountedRef.current) { setError(cause instanceof Error ? cause : new Error(String(cause))); } return false; } }, []); const exit = useCallback(async (): Promise<boolean> => { if ( typeof document === "undefined" || typeof document.exitFullscreen !== "function" || document.fullscreenElement === null ) { return false; } try { if (mountedRef.current) setError(null); await document.exitFullscreen(); return true; } catch (cause) { if (mountedRef.current) { setError(cause instanceof Error ? cause : new Error(String(cause))); } return false; } }, []); const toggle = useCallback( (): Promise<boolean> => (fullscreenRef.current ? exit() : enter()), [enter, exit], );
return { enter, error, exit, isFullscreen, isSupported, toggle };};Download
Section titled “Download”wget -O src/hooks/useFullscreen.ts https://raw.githubusercontent.com/jrTilak/lazykit/HEAD/registry/react-hooks/useFullscreen.tstemp_dir="$(mktemp -d)" && bunx degit jrTilak/lazykit/registry/react-hooks "$temp_dir" && mv "$temp_dir/useFullscreen.ts" src/hooks/useFullscreen.ts && rm -r "$temp_dir"Examples
Section titled “Examples”import { useRef } from "react";
import { useFullscreen } from "./useFullscreen";
export const FullscreenExample = () => { const ref = useRef<HTMLDivElement>(null); const { enter, exit, isFullscreen, isSupported } = useFullscreen(ref);
return ( <div ref={ref}> <p>{isFullscreen ? "Fullscreen" : "Windowed"}</p> <button type="button" disabled={!isSupported} onClick={() => void (isFullscreen ? exit() : enter())} > Toggle fullscreen </button> </div> );};ref(RefObject<ElementType | null>) — Optional fullscreen target. The document root is used only when the argument is omitted; a supplied empty ref is not redirected to the root.
Returns
Section titled “Returns”UseFullscreenReturn with state, support, error, and async enter, exit, and
toggle controls. Controls resolve false when unavailable or rejected.