🚦💥𝗗𝗶𝗳𝗳𝗲𝗿𝗲𝗻𝗰𝗲 𝗯𝗲𝘁𝘄𝗲𝗲𝗻 𝗗𝗲𝗳𝗮𝘂𝗹𝘁 𝗖𝗵𝗮𝗻𝗴𝗲 𝗗𝗲𝘁𝗲𝗰𝘁𝗶𝗼𝗻 𝗮𝗻𝗱 𝗢𝗻𝗣𝘂𝘀𝗵 𝗶𝗻 𝗮 𝟭𝟬𝟬% 𝘀𝗶𝗴𝗻𝗮𝗹𝘀-𝗯𝗮𝘀𝗲𝗱 𝗭𝗼𝗻𝗲𝗹𝗲𝘀𝘀 𝗮𝗽𝗽𝗹𝗶𝗰𝗮𝘁𝗶𝗼𝗻?
In practice, no meaningful difference. But still very interesting to know why:
𝗛𝗼𝘄 𝘀𝗶𝗴𝗻𝗮𝗹𝘀 𝗱𝗿𝗶𝘃𝗲 𝗰𝗵𝗮𝗻𝗴𝗲 𝗱𝗲𝘁𝗲𝗰𝘁𝗶𝗼𝗻
When a signal is read in a template, Angular's reactive graph records that dependency. When the signal updates, Angular internally calls markForCheck() on that component — regardless of its change detection strategy.
Both Default and OnPush get marked dirty and re-rendered by the same mechanism.
𝗪𝗵𝗮𝘁 𝗢𝗻𝗣𝘂𝘀𝗵 𝘄𝗮𝘀 𝗮𝗰𝘁𝘂𝗮𝗹𝗹𝘆 𝘀𝗼𝗹𝘃𝗶𝗻𝗴
OnPush was designed to opt out of the Zone.js-driven "check everything" cycle.
Without it, Zone.js would patch every setTimeout, Promise, XHR, etc. and trigger a full top-down tree walk.
OnPush let you say "only check me if my inputs changed or Iwas explicitly marked dirty."
In a zoneless app, that problem doesn't exist. There is no global tick.
Either:
1 - A signal marks a component dirty
2 - Or you mark the component has dirty yourself explicitly
Notice that the async pipe will also mark the component as dirty, but here I'm assuming a signals-only app.
In a signal-based zoneless app, there's no runaway Zone.js cycle to protect against.
𝗪𝗵𝗮𝘁 𝗵𝗮𝗽𝗽𝗲𝗻𝘀 𝗶𝗳 𝘀𝗶𝗴𝗻𝗮𝗹𝘀 𝗮𝗿𝗲 𝗯𝘆𝗽𝗮𝘀𝘀𝗲𝗱
In this exact type of application, if you accidentally bypass signals — a direct property mutation, a class field updated without signal.set(), etc. — nothing happens.
This hold true for both Default and OnPush - again this is true for a zoneless application specifically.
In a disciplined signals-only codebase that's actually a non-issue - every data modification goes via a signal.
𝗔𝗻𝗴𝘂𝗹𝗮𝗿'𝘀 𝗱𝗶𝗿𝗲𝗰𝘁𝗶𝗼𝗻
In the long term, it looks like fine-grained signal reactivity replaces the more coarse-grained Default/OnPush distinction entirely.
The more concrete near-term step is
github.com/angular/angular/d…,
which proposes making OnPush the schematic default in Angular 22 — because in a signals-first world, Default becomes the odd choice rather than OnPush.
#angular #angular21 #angular22 #onpush #changedetection