DEV Community

Raju Dandigam
Raju Dandigam

Posted on

10 1 2 1 1

Angular Signals vs Observables: A Deep Dive into Modern Reactivity

With the release of Angular 16, the framework introduced a powerful new reactive primitive: signals. Signals provide a new way to manage reactivity in your applications, enabling more predictable and efficient UI updates. But how do they compare with the well-established observables? When should you use one over the other? And how do features like computed() come into play?

In this post, we’ll explore Angular signals in depth, compare them with observables, and provide practical use cases to guide your decision-making.

🚦 What Are Angular Signals?

Signals are reactive values that notify dependents when their value changes. They are designed to be synchronous, fine-grained, and easy to use.

A signal is created using the signal() function and acts as a state container:

import { signal } from '@angular/core';

const count = signal(0);

console.log(count());   // Reading value: 0
count.set(5);           // Setting value
count.update(c => c + 1); // Updating value: 6
Enter fullscreen mode Exit fullscreen mode

Key Characteristics:

  • Synchronous: Reading a signal gives you its value immediately.
  • Dependency Tracking: When used inside a computed signal or an effect, it registers itself as a dependency.
  • Reactive: Automatically propagates changes to anything that depends on it.

🧮 Using computed() with Signals

One of the most powerful aspects of signals is the computed() function. It allows you to create derived signals, similar to computed properties in other reactive frameworks like Vue.js.

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

const price = signal(100);
const quantity = signal(2);

const total = computed(() => price() * quantity());

console.log(total()); // Output: 200

quantity.set(3);
console.log(total()); // Output: 300
Enter fullscreen mode Exit fullscreen mode

The computed signal automatically recalculates when any of its dependencies change. No manual subscriptions are needed.

🔁 Signals vs Observables

Let’s compare Angular signals with RxJS observables across different dimensions:
Image description

📌 When to Use Signals

✅ Use Signals When:

  • Managing local UI state (e.g. toggle, counters, forms)
  • Creating computed values that derive from other signals
  • You need fine-grained change detection (only dependent parts re-render)
  • You want simpler and more declarative reactivity

Example: Component-local state

@Component({
  selector: 'app-counter',
  template: `
    <button (click)="increment()">Add</button>
    <p>Count: {{ count() }}</p>
  `
})
export class CounterComponent {
  count = signal(0);

  increment() {
    this.count.update(c => c + 1);
  }
}
Enter fullscreen mode Exit fullscreen mode

🚫 Avoid Signals When:

  • You’re dealing with asynchronous data (e.g. HTTP requests, WebSocket streams)
  • You need to compose multiple async streams using RxJS operators (mergeMap, switchMap, etc.)
  • You’re integrating with libraries heavily based on RxJS

🌐 When to Use Observables

✅ Use Observables When:

  • Working with asynchronous streams (e.g. user inputs, API responses)
  • Using RxJS operators to transform streams
  • Handling event-based programming (e.g. WebSockets, form events)
  • Managing global app state with side effects (e.g. NgRx)

Example: Async HTTP data

@Component({
  selector: 'app-data',
  template: `
    <ng-container *ngIf="data$ | async as data">
      <p>{{ data.title }}</p>
    </ng-container>
  `
})
export class DataComponent {
  data$ = this.http.get('/api/data');

  constructor(private http: HttpClient) {}
}
Enter fullscreen mode Exit fullscreen mode

🔄 Interoperability: Signals and Observables

Angular provides utility functions to convert between signals and observables:

toSignal() – Observable to Signal

import { toSignal } from '@angular/core/rxjs-interop';

const signalFromObservable = toSignal(myObservable$, { initialValue: null });
Enter fullscreen mode Exit fullscreen mode

toObservable() – Signal to Observable

import { toObservable } from '@angular/core';

const observableFromSignal = toObservable(mySignal);
Enter fullscreen mode Exit fullscreen mode

This enables seamless integration of both paradigms.

💡 Use Case Summary

Image description

🧠 Final Thoughts

Signals represent a shift toward synchronous, declarative state management in Angular. While Observables will always have a place—especially for async operations—signals make state management in UI components much simpler and more intuitive.

Use signals for local, synchronous UI state and computed values. Use observables for asynchronous workflows and complex stream operations. And when needed, bridge the two with Angular’s interop utilities.

The future of Angular is signal-first but observables aren’t going anywhere. Choosing the right tool for the job is key.

Build seamlessly, securely, and flexibly with MongoDB Atlas. Try free.

Build seamlessly, securely, and flexibly with MongoDB Atlas. Try free.

MongoDB Atlas lets you build and run modern apps in 125+ regions across AWS, Azure, and Google Cloud. Multi-cloud clusters distribute data seamlessly and auto-failover between providers for high availability and flexibility. Start free!

Learn More

Top comments (0)

👋 Kindness is contagious

Explore this insightful write-up, celebrated by our thriving DEV Community. Developers everywhere are invited to contribute and elevate our shared expertise.

A simple "thank you" can brighten someone’s day—leave your appreciation in the comments!

On DEV, knowledge-sharing fuels our progress and strengthens our community ties. Found this useful? A quick thank you to the author makes all the difference.

Okay