isStandalone
Determines if the app is running in standalone (PWA) mode. Works for modern browsers with navigator.standalone
(iOS Safari) or display-mode: standalone
via matchMedia
.
1/**
2 * Determines if the app is running in standalone (PWA) mode.
3 *
4 * Works for modern browsers with 'navigator.standalone' (iOS Safari)
5 * or 'display-mode: standalone' via matchMedia.
6 *
7 * @returns True if app is in standalone mode.
8 */
9export function isStandalone(): boolean {
10 if (typeof window === 'undefined') return false;
11 return (
12 (window.matchMedia?.('(display-mode: standalone)').matches) ||
13 (navigator as any).standalone === true
14 );
15}
Cross-Platform PWA Support
Covers both modern standards (
display-mode: standalone
) and legacy iOS Safari (navigator.standalone
), ensuring broader compatibility.Non-Intrusive Detection
Uses passive checks without requiring explicit permissions or impacting runtime behavior.
Environment-Safe Guarding
Validates
window
availability to avoid runtime errors in SSR or Node environments.Minimal Overhead
Lightweight and efficient — uses built-in browser capabilities with no DOM or network interaction.
Tests | Examples
1const originalMatchMedia = window.matchMedia;
2const originalNavigator = navigator;
3
4afterEach(() => {
5 window.matchMedia = originalMatchMedia;
6 // @ts-ignore
7 global.navigator = originalNavigator;
8});
9
10test('returns true if matchMedia detects standalone mode', () => {
11 window.matchMedia = jest.fn().mockImplementation(query => ({
12 matches: query === '(display-mode: standalone)',
13 media: query,
14 onchange: null,
15 addListener: jest.fn(),
16 removeListener: jest.fn(),
17 addEventListener: jest.fn(),
18 removeEventListener: jest.fn(),
19 dispatchEvent: jest.fn(),
20 }));
21 expect(isStandalone()).toBe(true);
22});
23
24test('returns true if navigator.standalone is true (iOS)', () => {
25 // @ts-ignore
26 global.navigator = { standalone: true };
27 window.matchMedia = jest.fn().mockReturnValue({ matches: false });
28 expect(isStandalone()).toBe(true);
29});
30
31test('returns false if neither matchMedia nor standalone apply', () => {
32 // @ts-ignore
33 global.navigator = { standalone: false };
34 window.matchMedia = jest.fn().mockReturnValue({ matches: false });
35 expect(isStandalone()).toBe(false);
36});
Common Use Cases
Conditional UI Adjustments for PWAs
Hide navigation bars or add gestures tailored to full-screen environments.
Analytics for Install Mode Usage
Track how many users are running your app as a standalone PWA versus in a browser tab.
Feature Restrictions or Enhancements
Enable or disable certain features depending on whether the app is installed.
Prompting Reinstallation or Update
Show installation banners or update messages only when the app is not in standalone mode.