Angular has always embraced powerful reactive programming concepts — from change detection to RxJS-based data flows. But with the introduction of Signals, Angular now offers a simpler and more intuitive way to handle reactivity natively, without relying entirely on RxJS or @Input() bindings.

In this post, we’ll explore what Angular Signals are, how they work, and when you should use them in your applications.


⚡ What Are Signals in Angular?

Signals are a new reactive primitive introduced in Angular to track and respond to changes in state. Unlike RxJS Observables, which rely on subscriptions and streams, Signals offer a push-based, dependency-tracked model, making change detection more predictable and efficient.

Think of them as reactive variables that automatically update anything that depends on them.


🛠️ How to Use Signals

Angular’s Signals API is available via the @angular/core package.

Defining a Signal

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

const count = signal(0);

Reading a Signal

console.log(count()); // prints 0

Updating a Signal

count.set(1);
count.update(prev => prev + 1);

🧠 Signals vs. Observables

Feature Signals RxJS Observables
Syntax Simple, getter-like Subscription-based
Reactivity Pull-based (automatic tracking) Push-based
Async support No (designed for sync state) Yes (built for async streams)
Complexity Low Higher, especially for beginners
Use case Local/component state Streams, HTTP, events

Use Signals for: local component state, reactive templates, UI counters, form flags, etc.
Use Observables for: HTTP calls, user input streams, event handling, complex async logic.

🧪 Example: Counter Component Using Signals

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

@Component({
  selector: 'app-counter',
  template: `
    Counter: {{ count() }}
    Increment
  `
})
export class CounterComponent {
  count = signal(0);

  increment() {
    this.count.update(c => c + 1);
  }
}

No need for ChangeDetectorRef, @Input(), or EventEmitter — the UI auto-updates when the signal changes.

🧬 Computed and Effects

Angular also provides computed() and effect() APIs for more advanced logic.

computed() — derived values

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

const count = signal(2);
const double = computed(() => count() * 2);

effect() — run side effects

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

effect(() => {
  console.log('Count changed to:', count());
});

Signals mark a major evolution in Angular’s reactivity system. They're lightweight, easy to learn, and offer fine-grained reactivity without sacrificing performance or clarity. Combined with computed and effect, they bring Angular closer to the reactivity model of frameworks like SolidJS or Vue.

If you're building new Angular apps, or looking to refactor existing components, Signals offer a clean, modern alternative to traditional state management approaches.