Zoneless change detection and render lifecycle
Zoneless is the default in Angular 21 and later. Angular refreshes views from known notifications instead of patching every browser async API through Zone.js.
Notifications
Angular learns a view may need work when a template-read signal
changes, an input is set, a listener runs, AsyncPipe emits,
a marked view is attached, or code calls markForCheck.
Code that mutates an untracked plain field after a timer may not update the view. Make state visible through signals, inputs, or supported notification.
OnPush
OnPush remains useful because it encourages explicit data flow and zoneless- compatible components. It is not a magic performance flag and does not repair mutated inputs.
Lifecycle
Use constructor/field initialization for dependency setup,
ngOnInit for input- dependent initialization, and
ngOnDestroy or DestroyRef for cleanup. Avoid
work in frequently invoked checked hooks.
Render callbacks
afterNextRender and afterEveryRender
coordinate DOM-dependent work after rendering. Keep browser-only APIs
guarded for SSR. Use cleanup when the callback owns observers or
third-party instances.
Expression changed errors
These errors expose a state mutation during a check that made the
rendered result unstable. Fix ownership and timing rather than adding
arbitrary setTimeout.
Performance model
Signals reduce the affected graph; component boundaries, stable identity, deferred code, DOM size, JavaScript work, and network latency still matter. Measure with browser performance tools and Angular DevTools.
Feynman check
Zoneless does not mean “Angular never checks.” It means Angular checks in response to explicit notifications. Name the notification for every state update on one page.