Signals are a reactivity primitive introduced in Angular to make state management more declarative, efficient, and explicit. They provide a new way to handle and react to changes in data, much like observables but with synchronous and fine-grained reactivity, ideal for template-driven UIs.

Introduced in Angular 16, Signals aim to replace some use-cases of RxJS (like BehaviorSubject) in component state management by providing a simpler and zone-less reactivity model.

Conceptual Understanding

A value is held inside a container that is referred to as a Signal:

  • Tracks its dependencies automatically when read.
  • Notifies consumers when it is updated.
  • Can be used directly in templates and logic without subscriptions or manual unsubscriptions.

Analogy

A signal behaves how a reactive variable does:

  • Reading it establishes a dependency.
  • Updating it notifies everything that depends on it.

Types of Signals in Angular

Angular provides three primary ways to work with signals:

TypeDescription
signal()The basic writable signal. Used for storing values.
computed()A derived signal that computes values from other signals.
effect()A side-effect that runs automatically when dependent signals change.

Signal API Overview

Writable Signal — signal()

import { signal } from '@angular/core';
const count = signal(0); // Creates a signal with initial value 0
count(); // GET: returns 0
count.set(10); // SET: changes value to 10
count.update(v => v + 1); // Increments count

Computed Signal — computed()

import { computed } from '@angular/core';
const count = signal(2);
const double = computed(() => count() * 2);
console.log(double()); // 4
count.set(5);
console.log(double()); // 10 (automatically recalculates)
  • Automatically recalculates when dependencies change.
  • Pure function, no side-effects allowed inside.

Effect — effect()

import { effect } from '@angular/core';
const count = signal(0);
effect(() => { console.log(`Count changed to: ${count()}`);
});
count.set(5); // Automatically triggers the effect
  • Use for side-effects: console logs, HTTP calls, DOM manipulation, etc.
  • Automatically runs when dependent signal changes.

Real-life Example: Counter Component with Signals

import { Component, signal, computed, effect } from '@angular/core';
@Component({ selector: 'app-counter', template: ` <h2>Count: {{ count() }}</h2> <h3>Double: {{ doubleCount() }}</h3> <button (click)="increment()">Increment</button> `
})
export class CounterComponent { count = signal(0); // Writable signal doubleCount = computed(() => this.count() * 2); // Derived signal constructor() { effect(() => { console.log('Count updated to:', this.count()); }); } increment() { this.count.update(c => c + 1); }
}

Key Benefits of Signals

FeatureDescription
SynchronousUpdates happen immediately (no need to subscribe/pipe).
Fine-grainedOnly dependent parts re-render.
No ZonesWorks without NgZone (good for performance).
Simple APIEasier learning curve than RxJS.
Built-in Cleanupeffect() handles cleanup on its own.

Signals vs Observables (RxJS)

FeatureSignalsObservables
Push/PullPull-basedPush-based
AsyncMostly syncFully async
SimplicityEasier APIMore powerful but complex
CleanupAuto-cleanupManual unsubscribe
Best ForUI StateStreams, async data

Use Cases

  • Local component state (counter, toggles, forms)
  • Derived data in templates
  • ViewModel composition
  • Replacing BehaviorSubject in components

Angular Signal Lifecycle & Change Detection

  • Signals track where they’re used.
  • Change detection is triggered only where needed (fine-grained).
  • This allows zone-less rendering and better performance.

Conclusion

Angular Signals offer a simpler, more efficient way to manage UI state with fine-grained reactivity and no need for zones. With built-in dependency tracking and a clean API, they streamline component logic and performance. To leverage these cutting-edge features, it’s wise to hire Angular developer experts familiar with Signals and modern Angular best practices.