useDocumentVisibility
Creates a signal that tracks whether the document/tab is visible or hidden. The signal updates when the user switches tabs or minimizes the window.
MDN Reference: Page Visibility API
When to Use
Use useDocumentVisibility when you want to pause or resume activity based on tab focus — for example, pausing animations, stopping polling, or pausing video playback when the user switches away.
Usage
typescript
import { useDocumentVisibility } from 'ng-reactive-utils';
@Component({
template: `<h1>Tab currently visible: {{ isVisible() }}</h1>`,
})
class ExampleComponent {
isVisible = useDocumentVisibility();
}Pause Polling When Hidden
typescript
import { useDocumentVisibility } from 'ng-reactive-utils';
@Component({ template: `...` })
class DashboardComponent {
isVisible = useDocumentVisibility();
constructor() {
effect(() => {
if (this.isVisible()) {
this.startPolling();
} else {
this.stopPolling();
}
});
}
}Parameters
This composable takes no parameters.
Returns
Signal<boolean> — A readonly signal that is true when the page is visible, false when hidden (tab is backgrounded or window is minimized). Defaults to true on the server (SSR).
Notes
- Returned signal is readonly to prevent direct manipulation
- Uses
createSharedComposableinternally so there is only one shared instance at a time; event listeners are torn down automatically when no more consumers exist - SSR safe: defaults to
trueon the server wheredocumentis not available
Source
ts
import { signal, inject, PLATFORM_ID } from '@angular/core';
import { DOCUMENT, isPlatformBrowser } from '@angular/common';
import { createSharedComposable } from '../../../utils/create-shared-composable/create-shared-composable';
/*
* Creates a signal that tracks whether the document/tab is visible or hidden.
* The signal updates when the user switches tabs or minimizes the window.
*
* On the server, returns `true` (visible) by default and updates to actual value once hydrated on the client.
*
* Example:
*
* const isVisible = useDocumentVisibility();
*
* // Use in template
* @if (isVisible()) {
* <div>Tab is visible</div>
* } @else {
* <div>Tab is hidden</div>
* }
*/
export const useDocumentVisibility = createSharedComposable(() => {
const document = inject(DOCUMENT);
const platformId = inject(PLATFORM_ID);
const isBrowser = isPlatformBrowser(platformId);
// On server, default to visible (true). On client, use actual document.hidden state
const getInitialVisibility = () => (isBrowser ? !document.hidden : true);
const visibilitySignal = signal<boolean>(getInitialVisibility());
const handleVisibilityChange = () => visibilitySignal.set(!document.hidden);
// Only set up event listeners in the browser.
// visibilitychange fires on document (not window) per the Page Visibility API spec.
if (isBrowser) {
document.addEventListener('visibilitychange', handleVisibilityChange);
}
return {
value: visibilitySignal.asReadonly(),
cleanup: () => {
if (isBrowser) {
document.removeEventListener('visibilitychange', handleVisibilityChange);
}
},
};
});