How to set up zone flags
Legacy guidance — since Angular v21, change detection is zoneless by default and Zone.js is dropped from the default bundle. This page documents a legacy approach; still-zoneful may still need it. For new work, prefer the modern approach. See Zoneless & how Zone.js affected change detection for the full picture.
Since Angular v21, change detection is zoneless by default and Zone.js is dropped from the default bundle, a default v21 app has no Zone.js to configure, so there are no zone flags to set up. Follow this recipe only if your app still runs Zone.js (Angular <21, or v21 with Zone.js opted back in). For new applications, prefer the native zoneless path; see Zoneless & how Zone.js affected change detection.
Goal
In a Zone.js application, stop Zone.js from monkey-patching selected browser APIs, so those APIs no longer trigger change detection. You do this by setting zone flags before Zone.js initializes.
Why order matters
Zone.js reads its flags off the window object the first time it initializes,
so the flag code must run before the zone.js import. Put the flags in their
own module and import it first. Do not set a flag inline right above the
zone.js import, because bundlers hoist all import statements to the top of
the file, ahead of any executable statements:
// ❌ hoisted: the zone.js import runs before this assignment
window.__Zone_disable_XHR = true;
import 'zone.js';
// ✅ the flags module is imported (and runs) before zone.js
import './zone-flags';
import 'zone.js';
Steps (vanilla)
Create
zone-flags.tsand set the flags directly onwindow:(window as any).__Zone_disable_requestAnimationFrame = true;
(window as any).__Zone_disable_timers = true;
(window as any).__zone_symbol__UNPATCHED_EVENTS = ['load', 'error', 'close', 'open'];
(window as any).__Zone_disable_XHR = true;Import
zone-flags.tsbeforezone.js. In a Zone.js v21 app, Zone.js is opted in as a polyfill in the build target rather than through apolyfills.tsfile; add both imports to the target'spolyfillsarray inangular.json, keepingzone-flagsahead ofzone.js:// angular.json → architect.build.options
"polyfills": ["src/zone-flags.ts", "zone.js"]In an older CLI project that still has a
polyfills.ts, put the imports there in the same order:// polyfills.ts
import './zone-flags';
import 'zone.js';Import
zone.js, notzone.js/dist/zone; the/dist/zonedeep path is obsolete.
Steps (@rx-angular/cdk/zone-configurations)
The zoneConfig helper gives you typed methods, autocompletion, and an
assertion that errors if the flags run in the wrong order.
Create
zone-flags.tsusingzoneConfig:import { zoneConfig } from '@rx-angular/cdk/zone-configurations';
zoneConfig.global.disable.requestAnimationFrame();
zoneConfig.global.disable.timers();
zoneConfig.unpatchXHR();Disabling XHR patching is the top-level convenience method
zoneConfig.unpatchXHR(): it disables the globalXHRpatch and unpatches the XHR events in one call. There is nozoneConfig.events.disableXHR(); theevents.disablescope exposes onlyUNPATCHED_EVENTS([...])andPASSIVE_EVENTS([...]).Import
zone-flags.tsbeforezone.js, exactly as in the vanilla steps above (angular.jsonpolyfills array,zone-flagsfirst).
Runtime settings (after Zone.js)
Runtime settings must run after Zone.js loads. Put them in a separate module
imported after zone.js:
// zone-runtime.ts
import { zoneConfig } from '@rx-angular/cdk/zone-configurations';
zoneConfig.runtime.disable.ignoreConsoleErrorUncaughtError();
// angular.json → polyfills: zone-runtime after zone.js
"polyfills": ["src/zone-flags.ts", "zone.js", "src/zone-runtime.ts"]
Result
The flagged APIs are no longer patched by Zone.js, so they stop triggering change
detection. If the flags run in the wrong order, zoneConfig logs a console.error; check the
console. You can confirm the active flags with the debug helper (see below).
See also
- Reference:
zoneConfig - How-to: Debug zone flags
- Concept (legacy context): Zoneless & how Zone.js affected change detection