What’s the untracked function? [Angular Signals]

What’s the untracked function? [Angular Signals]

Learn how Angular's computed() function derives reactive values from signals and why it plays a key role in building high-performance, signal-based applications with cleaner and more predictable state management.

Alain Chautard

Alain Chautard

June 16, 2026

In a past post, I discussed the importance of signal-based components and how such components will change the way we architect Angular applications for better performance.

I’ve also mentioned the computed()function used to derive values from any number of signals. For instance:

      name = signal("Al");
lastName = signal("Test");

fullName = computed(() => this.name() + " " + this.lastName());

    

In the above example, changing the value of either name or lastName will automatically update the value of fullName.

What happens with computed() is that any signal read inside the computed function becomes a dependency of the new computed signal.

Angular Certification Exam On paper, this sounds great, but what if we need to compute a value when a signal A changes and just read the current value of another signal B when A changes? This means that updating A would trigger the computation, not updating B.

In other words, what if we wanted fullName to update only when name changes, but not when lastName changes?

The solution to that problem is a function called untracked:

      import { computed, signal, untracked } from '@angular/core';

fullName = computed(() => this.name() + " " + untracked(this.lastName));

    

untracked allows reading the value of a signal without making such signal a dependency of our computed signal or effect.

Let’s consider the following example also accessible on Stackblitz:

      name = signal('Angular');
counter = signal(0);

info = computed(
    () =>
      `The name is now "${this.name()}" and the 
      counter value was ${untracked(this.counter)} when 
      the name changed.`
 );

    

In this example, increasing the counter doesn’t trigger a new computation of info. Only a new name does so:

Note that sometimes, we might read another signal without being aware because such signal is read by a service or another dependency. In that case, this other signal would also become a dependency of our computed signal or effect.

The solution in that scenario relies on using untracked again with a slightly different syntax, passing a function it it:

      effect(() => {
  const name = this.name();
  untracked(() => {
    // If the `counterService` reads other signals, they won't 
    // become dependencies of this effect.
    // Only this.name() is a trigger of this effect
    this.counterService.count(`User name changed to ${name}`);
  });
});

    

Any code called in the function passed to untracked could be reading other signals, and such signals won’t become dependencies of the effect/computed function.

This can be very helpful, for instance, to avoid infinite loops of signal updates, and it should be a best practice to implement even if such code doesn’t read other signals right now.

For instance, in the above example, this.counterService.count() might not read other signals as-is, but another developer could add signals in that implementation two months from now and have no idea that they created a dependency on an effect in a different part of the application.

As a result, I’d recommend using untracked every time a computed function or effect calls external code unless we know that we want to depend on other signals read by such external code.

More certificates.dev articles

Get the latest news and updates on developer certifications. Content is updated regularly, so please make sure to bookmark this page or sign up to get the latest content directly in your inbox.