takeUntilDestroy in Angular v16
takeUntilDestroy is a new feature coming in Angular 16. In this article, we will explore how it works, and learn how to use it.

When subscribing to observables (especially in our components) we have to unsubscribe on destroy to prevent any memory leaks in our application. We can do it in a few ways, and now a new sheriff is in town! In most cases, it is probably the easiest to use since it does many things automatically.
Previous methods of unsubscribing from subscriptions added a lot of boilerplate to our classes, which reduced readability. For example, consider the following subscription that needs to be safely handled:
export class Component implements OnInit {
data;
ngOnInit(): void {
this.service.getData().subscribe(
response => this.data = response.
)
}
}
We could use takeUntil
operator with additional subject
:
export class Component implements OnInit, OnDestroy {
data;
destroyed = new Subject()
ngOnInit(): void {
this.service.getData()
.pipe(
takeUntil(this.destroyed),
)
.subscribe(
response => this.data = response
)
}
ngOnDestroy(): void {
this.destroyed.next();
this.destroyed.complete();
}
}
Or any other method to deal with an open subscription — there is many of them, but as long as you are calling the subscribe
in the component’s class you will end up with many boilerplate code.
Luckily that’s when new operator comes into play in Angular 16 — the takeUntilDestroy
. This pipe-able operator functions similarly to the example above with takeUntil(this.destroyed)
, but with almost zero additional code required!
Injectable OnDestroy
Angular 16 introduced a flexible ngOnDestroy
, which makes the OnDestroy
hook injectable.
destroyRef = inject(DestroyRef);
This allows us to inject it into our components instead of using it as a method. As a result, we can modify our takeUntil
example to something like this:
export class Component implements OnInit {
destroyRef = inject(DestroyRef);
ngOnInit(): void {
const destroyed = new Subject();
this.destroyRef.onDestroy(() => {
destroyed.next();
destroyed.complete();
});
this.service.getData()
.pipe(takeUntil(destroyed))
.subscribe(response => this.data = response)
}
}
This basically means that we don't need to implement the ngOnDestroy
method in our component. All the "additional" code can be wrapped within a pipe-able operator, which is what happened!
takeUntilDestroy
Now we can use the takeUntilDestroy
operator, which is super convenient. Just add it to the pipe without passing anything, and it will automatically pick up the right OnDestroy
for the current context — using injectable OnDestroy.
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
export class Component implements OnInit{
data;
constructor(private service: DataService) {
this.service.getData()
.pipe(takeUntilDestroyed())
.subscribe(response => this.data = response)
}
}
That is all!
Passing OnDestroy reference
In some cases, we may want to react to the destroy event of another component. For example, consider a scenario where a parent component has a subscription that needs to remain active as long as the child component is on the screen. In this case, we can inject DestroyRef
in the child component:
export class Child {
destroyRef = inject(DestroyRef);
}
We can use the new takeUntilDestroyed operator in the parent component to close the subscription by passing the reference to the DestroyRef
of the child component. Here is an example implementation for the parent component:
export class Parent {
@ViewChild(Child) child: Child;
ngOnInit(): void {
interval(1000)
.pipe(takeUntilDestroyed(this.child.destroyRef))
.subscribe((count) => console.log(count));
}
}
The count will be logged to the console as long as the Child
component exists. Upon its destruction, the subscription in the Parent
will be stopped.
Have fun and enjoy Angular 16 features! 🤓