Introduction
For years, Angular has depended on Observables and its Change Detection mechanism to handle reactivity. With the launch of Angular 16, a new reactive primitive called Signals was introduced, providing a more fine-grained, predictable, and efficient approach to managing state updates. Signals mark a significant evolution in Angular’s reactivity model, aiming to streamline state management while improving rendering performance and overall application efficiency
What Are Signals?
A Signal is a reactive container that holds a value and automatically notifies its dependents whenever that value changes. In contrast to Observables—which are push-based and typically asynchronous—Signals operate synchronously and provide more transparent state tracking.
Key Characteristics
Lightweight: They introduce very little overhead during creation and updates.
Fine-grained: Only the components or computations that depend on the changed value are updated, rather than triggering a full view refresh.
Predictable: State changes propagate immediately and in a consistent manner.
Compatible: They integrate smoothly with existing Angular features and APIs.
Why Angular Introduced Signals
Before Signals, Angular’s reactivity model leaned on:
Zone.js-driven change detection
@Input() property bindings
RxJS Observables
Manual state management techniques
While these tools were powerful, they came with notable drawbacks:
Entire component trees could re-render unnecessarily
Debugging reactive flows was often complicated
Performance tuning demanded deep expertise in change detection strategies
Signals solve these challenges by introducing fine-grained reactivity. Instead of re-rendering broadly, Angular now updates only the parts of the application that directly depend on the changed state.
Types of Signals
1) Writable Signals
Writable signals expose an API that allows you to modify their values directly. They are created by invoking the signal function and passing in the initial value as an argument.
Example:
import { signal } from '@angular/core';
const counter = signal(0);
counter.set(1); // updates value
counter.update(value => value + 1); // increments
Core Methods
| Method Name | Description |
|---|---|
| set(newValue) | The set() method replaces the current value of the signal with a new one. |
| update(fn) | The update() method modifies the signal’s value based on its current state. |
| asReadonly() | The asReadonly() method creates a read-only version of the signal. |
2) Computed Signals
Computed signals are reactive values that are automatically derived from one or more other signals. Instead of holding independent state, they act as pure functions of existing signals, recalculating only when their dependencies change.
Example:
import { signal, computed } from '@angular/core';
const price = signal(200);
const quantity = signal(4);
const total = computed(() => price() * quantity());
console.log(total()); // 800
If price or quantity changes, total is recalculated automatically. You don’t need to manually trigger updates—Angular handles the dependency tracking for you.
Important Behaviors
Lazy evaluation: Computed signals don’t run until they’re accessed.
Caching: Results are stored, so repeated reads don’t cause unnecessary recomputation.
Dependency awareness: They re-run only when one of their input signals changes.
Why They’re Useful
Simplify derived state (e.g., totals, filters, formatted values).
Reduce boilerplate compared to manual subscriptions or RxJS operators.
Improve performance by avoiding redundant recalculations.
Make reactive flows more predictable and easier to debug.
3) Effects
Effects are reactive functions that automatically run whenever the signals they depend on change. They’re designed to handle side effects—operations that go beyond pure state updates.
Example:
import { effect } from '@angular/core';
effect(() => {
console.log('Total changed:', total());
});
In this example, whenever total() changes, the effect executes and logs the new value.
Common Use Cases
Effects are ideal for tasks that need to respond to state changes but don’t belong in core business logic:
Logging application state changes
Triggering API calls when certain values update
Interacting with the DOM (e.g., animations, focus management)
Synchronizing state with local storage or external systems
Best Practices

Join the conversation! Your thoughts help the community grow.