How to unsubscribe in Angular (the 2026 guide)
Knowing how to unsubscribe in Angular is what keeps RxJS subscriptions from leaking memory. This guide covers every approach — the async pipe, takeUntilDestroyed, takeUntil, manual teardown — when each fits, the current best practice, and how to confirm you didn't miss one.
Do you always need to unsubscribe?
No — and this is the part most guides get wrong. You only need to unsubscribe from streams that don't complete on their own. An HttpClient call completes after one emission; a stream piped through take(1) or first() completes too. You do need to unsubscribe from long-lived streams: valueChanges, fromEvent, interval, route params, store selectors.
The ways to unsubscribe, ranked
1. The async pipe (preferred)
If the value is only used in the template, bind the Observable with | async and let Angular manage the subscription. Nothing to remember, nothing to leak.
@if (user$ | async; as user) {
{{ user.name }}
}2. takeUntilDestroyed() (Angular 16+)
For imperative subscriptions, pipe takeUntilDestroyed(). Called in an injection context it ties the stream to the component/service lifecycle automatically.
constructor() {
this.form.valueChanges
.pipe(takeUntilDestroyed())
.subscribe(v => this.save(v));
}Deep dive: takeUntil vs takeUntilDestroyed.
3. takeUntil(destroy$) (classic)
Before Angular 16, the idiom was a destroy Subject completed in ngOnDestroy. Still valid, more boilerplate.
private destroy$ = new Subject<void>();
ngOnInit() {
this.svc.data$.pipe(takeUntil(this.destroy$)).subscribe(/* … */);
}
ngOnDestroy() {
this.destroy$.next();
this.destroy$.complete();
}4. Manual unsubscribe / SubSink
Hold the Subscription and call unsubscribe() in ngOnDestroy, or collect several with a helper. See SubSink compared. Most error-prone — easiest to forget one.
5. @UntilDestroy() decorator
until-destroy removes the destroy-subject boilerplate via a decorator + untilDestroyed(this). Convenient; still opt-in per stream.
Best practice in 2026
- Template-only value → async pipe.
- Imperative subscription →
takeUntilDestroyed(). - Pre-16 codebase →
takeUntil(destroy$)or until-destroy. - Whatever you pick, apply it consistently — mixed conventions are where leaks hide.
How to know you didn't miss one
Every approach above is opt-in: it only protects streams you remember to wire. The silent failure is the one subscribe() without teardown. Run rxjs-leak-finder in dev — one line in main.ts — and it flags any subscription that outlived its owner, with the stack trace pointing at the exact line.
// main.ts (dev only)
import { isDevMode } from '@angular/core';
import { enableRxjsLeakDetector } from 'rxjs-leak-finder';
if (isDevMode()) {
enableRxjsLeakDetector();
}Background on the bug class itself: RxJS memory leaks explained.
FAQ
What happens if I don't unsubscribe in Angular?
For long-lived streams, the subscription and everything it captured stay in memory after the component is destroyed — a leak. Memory grows over time and stale callbacks keep firing.
Is the async pipe enough on its own?
For template-bound streams, yes. You still need a teardown strategy for anything you subscribe to in TypeScript.