Chapter 5: Signals & Reactivity

Signals & Reactivity is the big mental model shift in modern Angular.

This is what makes Angular feel fresh and fast in 2026. Signals are the new heart of reactivity — they replace a ton of the old RxJS + zone.js dance, especially inside components and templates.

We’ll go slow, with real examples you can paste into your project right now (assuming you have Angular 19+). I’ll explain like we’re debugging together on VS Code Live Share.

1. What are Signals? (The simple story)

A signal is just a tiny wrapper around a value that says: “Hey Angular, when someone reads me, remember that. When I change, tell ONLY the people who care.”

  • Reading a signal: count() (note the parentheses — it’s a function!)
  • Changing it: .set(newValue) or .update(old => old + 1)

There are three main APIs:

  • signal() → create a writable signal (mutable state)
  • computed() → create a readonly derived signal (automatic calculations)
  • effect() → run side effects when signals change (logging, analytics, imperative DOM, etc.)

Huge win: Signals are synchronous, fine-grained, and Zone.js-independent in many cases (especially with zoneless mode coming strong).

2. signal() — The writable one (your source of truth)

TypeScript

Template:

HTML

→ Click +1 → screen updates instantly, only the parts reading count() re-evaluate. No full change detection cycle needed if you’re using OnPush + signals.

Writable vs Readonly

  • WritableSignal<T> — has .set() and .update(). You own it, you mutate it.
  • Signal<T> (readonly) — only .() to read. No mutation methods.

You create readonly versions like this (great for encapsulation):

TypeScript

Important: Readonly prevents .set() / .update(), but if the value is an object/array, you can still mutate it deeply (e.g. user().name = ‘new’). Treat objects as immutable for safety — create new ones with spread or structuredClone.

3. computed() — Derived state magic (readonly + lazy + memoized)

Computed signals are read-only and auto-update when their dependencies change.

Key superpowers:

  • Lazy: Doesn’t run until first .() read
  • Memoized: Caches result until a dependency changes
  • Dynamic dependencies: Only tracks signals actually read in the last run

Example — let’s make a smart user profile card:

TypeScript

Template:

HTML

→ Change age.set(16) → isAdult(), status() auto-update → details() doesn’t depend on age until showDetails is true → perf win!

When to use computed vs plain method/getter?

  • Use computed() when it depends on signals → want reactivity
  • Use plain getter when it’s static or non-reactive
TypeScript

4. effect() — Side effects (last resort, but useful)

Effects run code when signals change — think logging, saving to localStorage, analytics, syncing to non-Angular APIs.

TypeScript

Rules from teacher:

  • Effects always run at least once on creation
  • They track signals read inside → re-run only when those change
  • Avoid using effects to copy data between signals — use computed() or new linkedSignal() instead
  • Prefer computed / linkedSignal / resource first
  • Effects are for imperative stuff (DOM manipulation outside Angular, third-party libs)

Untracked helper (read without dependency):

TypeScript

5. Why Signals Replace Most Observable Use Cases in Templates

In old Angular (pre-17):

  • Local state → BehaviorSubject / Subject + .next()
  • Template → async pipe: {{ count$ | async }}
  • Derived → combineLatest, map, shareReplay
  • Subscription hell, memory leaks if forget takeUntil

In 2026 with signals:

  • No subscription needed
  • No async pipe in 90% cases
  • Synchronous reads in templates → {{ count() }}
  • Fine-grained: Angular tracks exactly which signals are read in a template/computed → updates only affected DOM bits
  • Better perf with OnPush + zoneless future
  • Simpler mental model: no cold/hot observables, no operators for simple cases

When to still use RxJS/Observables (2026 reality)

  • Async streams: HTTP (but new resource() + toSignal() bridges it)
  • WebSockets, timers, mouse move streams, debounce/throttle
  • Complex operators (switchMap, exhaustMap, etc.)
  • Interop: toObservable(signal) and toSignal(observable)

Signals handle synchronous + derived UI state → RxJS for streams/events/async

Quick Cheat Sheet Table

API Mutable? Use Case Example Read Triggers Update?
signal() Yes Source of truth, user input, counters count() Yes (set/update)
computed() No Derived values, filters, formatting double() When deps change
effect() N/A Logging, sync to localStorage, 3rd-party N/A (side effect) When deps change
input() No Parent → child (readonly signal) name() When parent changes input

Mini Practice Task (do this!)

In your app.component.ts or a new component:

TypeScript

Template:

HTML

→ Change mood → only profile recomputes → smooth!

You’ve now got the core reactivity model of modern Angular.

Next is usually Services + Dependency Injection (where we share signals across components). Ready, or want more signal examples (forms integration, async with toSignal, linkedSignal intro, common pitfalls like reading signals outside reactive context)? Tell me! 😊

You may also like...

Leave a Reply

Your email address will not be published. Required fields are marked *