Angular's OnPush: Faster Apps by Doing Less

OnPush tells Angular to only re-render a component when its inputs change or its own events fire, not on every global event. It's a key performance boost for large apps, but mutating an input object directly won't trigger a re-render, causing a stale UI.
WHY IT EXISTS By default, Angular checks every component for changes after any browser event, timer, or data fetch. In large applications, this constant checking from the top of the component tree down can become a major performance bottleneck. ChangeDetectionStrategy.OnPush was created to give developers a way to opt out of this constant checking and define stricter, more performant rules for updates.
THE MENTAL MODEL Think of OnPush as changing a component's update subscription from "check me constantly" to "notify me only when it's important." Instead of Angular's default strategy of actively polling every component for changes, OnPush components are passive. They are considered "clean" and are skipped during the change detection cycle until a specific event explicitly marks them as "dirty" and in need of a check.
HOW IT WORKS A component using OnPush will only be checked and re-rendered if one of four conditions is met. First, one of its @Input() properties receives a new object or value reference. Second, an event handler is triggered within that component or one of its children (like a button click). Third, an observable bound to its template via the async pipe emits a new value. Fourth, you manually tell Angular to check the component using ChangeDetectorRef.markForCheck().
WHEN TO USE IT Use OnPush for any component whose state is primarily determined by its inputs, especially "presentational" or "dumb" components. It is the standard and most effective strategy when using state management libraries like NgRx or data services that provide immutable state objects. The goal should be to use OnPush as the default for all new components to build performant apps from the start.
WHEN NOT TO USE IT Avoid OnPush if your component's rendering depends on a shared, mutable service or some other global state that is not passed in via @Input or an observable with an async pipe. In such cases, the component has no signal that the external data has changed, and its view will become stale unless you manually inject ChangeDetectorRef to trigger updates, which can become complex.
ONE CANONICAL EXAMPLE Consider a UserProfile component with an @Input() user property. If you pass it a user object and then another component mutates a property (user.name = 'new name'), OnPush will not update the UI because the object reference for user has not changed. The correct way to trigger an update is to pass a completely new object, for example: this.user = { ...this.user, name: 'new name' };. This new reference is the signal OnPush needs to run change detection.
Read the original → angular.dev
Get five bites like this every day.
Tezvyn delivers a daily feed of 60-second tech bites with quizzes to lock in what you learn.