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.

Image of Datadog

Optimize UX with Real User Monitoring

Learn how Real User Monitoring (RUM) and Synthetic Testing provide full visibility into web and mobile performance. See best practices in action and discover why Datadog was named a Leader in the 2024 Gartner MQ for Digital Experience Monitoring.

Tap into UX Best Practices

Top comments (0)

Image of Datadog

Keep your GPUs in check

This cheatsheet shows how to use Datadog’s NVIDIA DCGM and Triton integrations to track GPU health, resource usage, and model performance—helping you optimize AI workloads and avoid hardware bottlenecks.

Get the Cheatsheet

👋 Kindness is contagious

Engage with a wealth of insights in this thoughtful article, cherished by the supportive DEV Community. Coders of every background are encouraged to bring their perspectives and bolster our collective wisdom.

A sincere “thank you” often brightens someone’s day—share yours in the comments below!

On DEV, the act of sharing knowledge eases our journey and forges stronger community ties. Found value in this? A quick thank-you to the author can make a world of difference.

Okay