Modern Angular: Signals, Standalone Components and SSR · Components, Signals and Data

Signals: computed, effect, linkedSignal and untracked

Model state with signals, derive values with computed, run side effects with effect, reset dependent state with linkedSignal, and read without tracking.

Create a free account to save this page, take notes and track your progress. Sign in

What you'll build

A product picker driven by signals: the list of products is a signal, the selected product is a linkedSignal that resets when the list changes but can still be set by the user, the price total is computed, and an effect saves the selection, reading a logger setting with untracked so it doesn't re-run when that setting changes.

The idea

  • signal(value): read with s(), write with set/update. Reads inside templates, computed and effect are tracked automatically.
  • computed: derived, cached, lazily recalculated.
  • effect: side effects (logging, storage, non-Angular APIs) that re-run when the signals they read change; they run asynchronously during change detection, not synchronously on set.
  • linkedSignal: writable state derived from another signal; it resets whenever the source changes.
  • untracked(() => ...): read a signal without creating a dependency.

The code

computed, effect, linkedSignal and untracked
import { Injector, computed, effect, linkedSignal, signal, untracked } from "@angular/core";
import { TestBed } from "@angular/core/testing";
import { BrowserTestingModule, platformBrowserTesting } from "@angular/platform-browser/testing";

TestBed.initTestEnvironment(BrowserTestingModule, platformBrowserTesting());
const injector = TestBed.inject(Injector);

const products = signal([{ name: "Mug", price: 8.5 }, { name: "Teapot", price: 24 }]);
const quantity = signal(1);
const selected = linkedSignal(() => products()[0]);          // resets when products changes
const total = computed(() => {
  console.log("  (computing total)");
  return selected().price * quantity();
});
const verboseLogs = signal(false);

effect(() => {
  const name = selected().name;                              // tracked
  const verbose = untracked(verboseLogs);                    // not tracked
  console.log(`effect: saved selection ${name}${verbose ? " (verbose)" : ""}`);
}, { injector });

console.log("total:", total());
console.log("total again:", total());                        // cached
TestBed.tick();                                              // effects run during change detection

selected.set(products()[1]);                                 // user picks Teapot
quantity.set(2);
console.log("total:", total());
TestBed.tick();

verboseLogs.set(true);                                       // untracked: the effect doesn't re-run
TestBed.tick();

products.set([{ name: "Kettle", price: 31.9 }]);             // new list: selection resets
console.log("selected after list change:", selected().name, "total:", total());
TestBed.tick();

Output

  (computing total)
total: 8.5
total again: 8.5
effect: saved selection Mug
  (computing total)
total: 48
effect: saved selection Teapot
  (computing total)
selected after list change: Kettle total: 63.8
effect: saved selection Kettle (verbose)

Run this on your computer — see “Get Started” for setup.

Common mistakes

  • Using effect to copy one signal into another; use computed or linkedSignal.
  • Expecting effects to run synchronously after set; they're scheduled and run during change detection.
  • Creating effects outside an injection context (no injector), which throws.
  • Mutating objects inside a signal (list().push(x)) instead of update with a new value; the signal doesn't notice.

Key points

  • signal for state, computed for derived values, effect for side effects.
  • linkedSignal for writable state that resets with its source.
  • untracked reads without creating dependencies.

Further reading: Signals overview · linkedSignal · Effects

Practice questions

When should you use linkedSignal instead of computed?

Answer

When the value must be derived from a source but also writable by the user, resetting whenever the source changes, such as a selection that resets when the list changes.

Why shouldn't you use effect to sync one signal into another?

Answer

It runs asynchronously, can cause extra change detection cycles and inconsistent intermediate states; computed or linkedSignal express derived state directly.

What does untracked do?

Answer

It reads a signal inside a reactive context without registering it as a dependency, so changes to it don't re-run the computed or effect.

Open in the app to mark lessons complete, take notes and track your progress.

Was this page helpful?