Skip to content
All essays
WebMarch 27, 202512 min

Angular Signals: Reactive State Management

Master Angular Signals for reactive state management. Learn computed signals, effects, and migration strategies.

Ü
Ümit Uz
Mobile & Full Stack Developer

Angular Signals provide a new way to handle reactive state with fine-grained reactivity and improved performance.

What Are Signals?

Signals are wrappers around values that notify interested consumers when the value changes.

Basic Signals

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

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

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

Signal Methods

typescript
// Create signal
const count = signal(0);

// Read value
console.log(count()); // 0

// Set value
count.set(5);

// Update based on previous value
count.update(value => value + 1);

// Mutate (for objects)
const user = signal({ name: 'John' });
user.mutate(value => value.name = 'Jane');

Computed Signals

Derive values from other signals:

typescript
@Component({
  selector: 'app-cart',
  template: `
    <p>Items: {{ items().length }}</p>
    <p>Total: ${{ total() }}</p>
  `
})
export class CartComponent {
  items = signal<CartItem[]>([]);

  total = computed(() =>
    this.items().reduce((sum, item) => sum + item.price, 0)
  );

  tax = computed(() => this.total() * 0.1);

  grandTotal = computed(() => this.total() + this.tax());
}

Effects

Run side effects when signals change:

typescript
@Component({ ... })
export class UserComponent implements OnDestroy {
  user = signal<User | null>(null);

  private destroyRef = inject(DestroyRef);

  constructor() {
    effect(() => {
      const user = this.user();
      if (user) {
        console.log('User changed:', user.name);
        localStorage.setItem('user', JSON.stringify(user));
      }
    }, { allowSignalWrites: true });
  }
}

Signal Inputs

typescript
@Component({
  selector: 'app-user-card',
  template: `
    <h2>{{ name() }}</h2>
    <p>{{ email() }}</p>
  `
})
export class UserCardComponent {
  name = input<string>('');
  email = input<string>('');

  user = computed(() => ({
    name: this.name(),
    email: this.email()
  }));
}

Required Inputs

typescript
export class UserCardComponent {
  name = input.required<string>();
  email = input.required<string>();
}

Signal Models (Two-Way Binding)

typescript
@Component({
  selector: 'app-input',
  template: `
    <input [(ngModel)]="value()" />
  `
})
export class InputComponent {
  value = model<string>('');
}

Signal Outputs

typescript
@Component({
  selector: 'app-button',
  template: `
    <button (click)="handleClick()">Click</button>
  `
})
export class ButtonComponent {
  clicked = output<void>();

  handleClick() {
    this.clicked.emit();
  }
}

Signals in Services

typescript
@Injectable({ providedIn: 'root' })
export class UserService {
  private users = signal<User[]>([]);
  private filter = signal<string>('');

  usersFiltered = computed(() =>
    this.users().filter(user =>
      user.name.includes(this.filter())
    )
  );

  loadUsers() {
    this.users.set([
      { id: 1, name: 'John' },
      { id: 2, name: 'Jane' }
    ]);
  }

  setFilter(filter: string) {
    this.filter.set(filter);
  }
}

RxJS Interop

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

@Component({ ... })
export class DataComponent {
  data = signal<Data[]>([]);

  // Signal to Observable
  data$ = toObservable(this.data);

  // Observable to Signal
  users = toSignal(
    this.http.get<User[]>('/api/users'),
    { initialValue: [] }
  );
}

Best Practices

  1. 1Use signals for local state - Component and service state
  2. 2Use computed for derived state - Calculated values
  3. 3Use effects for side effects - Logging, persistence
  4. 4Keep effects simple - Avoid complex logic
  5. 5Use input/output for components - Component communication

Angular Signals provide a modern, reactive approach to state management with excellent performance and developer experience.

Enjoyed this essay? Share it.

Related essays

Next essay
Angular RxJS: Reactive Programming