Angular’s Signals API, both normal signals and writable signals

Leader posted 1 min read

They basically help to manage reactive states—but they serve slightly different purposes.


What is a Signal in Angular?

A signal is a reactive primitive introduced in Angular 16+ to track state changes in a more declarative and efficient way — without requiring Observables or ChangeDetectorRef.


Writable Signal (signal())

It’s the most common type and can be updated directly.

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

const count = signal(0);

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

// Set a new value
count.set(1);

// Update with function
count.update(current => current + 1);

Use it when:

  • You need to read and write state.
  • You're managing local component state, like counters, form states, etc.

Read-only Signal (computed() or readonly())

This is a derived signal, typically created using computed() or readonly().

It is not writable directly.

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

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

console.log(double()); // 4

// double.set(10); ❌ Not allowed (read-only)

If you forcefully convert a writable signal to read-only, you use:

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

const state = signal('draft');
const readOnlyState = readonly(state);

// readOnlyState.set('published'); ❌ Not allowed

Use it when:

  • You want derived values or read-only state.
  • You want to expose state safely without allowing outside updates.

Summary Table

Feature signal() (Writable Signal) computed() / readonly() (Read-only Signal)
Can update value ✅ Yes ❌ No (derived or locked)
Can be used for state ✅ Yes ✅ As a derived state
Used for input value ✅ Often No direct input
Mutation-safe ⚠️ Needs care ✅ Safe from mutation
Ideal for State you read/write Derived state / exposed state without updates

Example in Angular Component

@Component({
  selector: 'my-counter',
  standalone: true,
  template: `
    <button (click)="increment()">Count: {{ count() }}</button>
    <p>Double: {{ doubleCount() }}</p>
  `
})
export class CounterComponent {
  count = signal(0); // Writable signal

  doubleCount = computed(() => this.count() * 2); // Read-only signal

  increment() {
    this.count.update(c => c + 1);
  }
}
If you read this far, tweet to the author to show them you care. Tweet a Thanks

Nice, clean explanation of writable vs read-only signals—really helpful for those diving into Angular’s reactivity model! One quick question: in larger apps, do you find yourself exposing a lot of state as readonly() to enforce immutability, or do you prefer managing access through service boundaries instead?

More Posts

Learn to build complete web apps by mastering both frontend and backend development technologies.

Sushant Gaurav - Jan 29

JavaScript Tricks for Efficient Developers and Smart Coding

Mainul - Nov 30, 2024

Leveraging service workers to enhance web app performance and reliability

Sunny - Jun 8

Top Coding Languages to Learn in 2025: Stay Ahead in Tech

Michael Liang - May 15

Web Performance Optimization with NgOptimizedImage in Angular

Sunny - Jun 9
chevron_left