Computed signals in Angular allow you to create reactive values that automatically update whenever their dependent signals change. They are read-only and ensure that your derived state always stays in sync with the source signals. Whenever a source signal changes, Angular recomputes the computed signal and updates any bindings that use it.
A computed signal is created using the computed() function from @angular/core. Inside computed() you write a function that reads other signals. The return value of that function becomes the value of the computed signal.
Key points:
computed(() => ...) to describe how to derive a value.firstName().firstName, price, tasks), never the computed ones.
// app.component.ts - using a computed signal for full name
import { Component, signal, computed } from '@angular/core';
@Component({
selector: 'app-root',
templateUrl: './app.component.html'
})
export class AppComponent {
firstName = signal('John');
lastName = signal('Doe');
fullName = computed(() => this.firstName() + ' ' + this.lastName());
}
<!-- app.component.html - template bound to the computed fullName -->
<p>First Name: {{ firstName() }}</p>
<p>Last Name: {{ lastName() }}</p>
<p>Full Name: {{ fullName() }}</p>
<button (click)="firstName.set('Jane')">Change First Name</button>
Initially, the UI shows:
First Name: JohnLast Name: DoeFull Name: John DoeWhen you click Change First Name, the firstName signal becomes 'Jane'. Angular automatically recomputes fullName to 'Jane Doe', and the template updates without any manual DOM handling.
// app.component.ts - computing the total price from price and quantity
import { Component, signal, computed } from '@angular/core';
@Component({
selector: 'app-root',
templateUrl: './app.component.html'
})
export class AppComponent {
price = signal(100);
quantity = signal(2);
total = computed(() => this.price() * this.quantity());
}
<!-- app.component.html - showing the total and updating quantity -->
<p>Price: {{ price() }}</p>
<p>Quantity: {{ quantity() }}</p>
<p>Total: {{ total() }}</p>
<button (click)="quantity.set(quantity() + 1)">Increase Quantity</button>
At the start, price() is 100 and quantity() is 2, so the computed signal total() becomes 200.
Each click on Increase Quantity calls quantity.set(quantity() + 1). The total computed signal automatically recalculates (for example: 3 × 100 = 300, 4 × 100 = 400, and so on) and the template updates accordingly.
// app.component.ts - splitting tasks into completed and remaining using computed signals
import { Component, signal, computed } from '@angular/core';
@Component({
selector: 'app-root',
templateUrl: './app.component.html'
})
export class AppComponent {
tasks = signal([
{ name: 'Learn Angular', done: true },
{ name: 'Build Project', done: false }
]);
completedTasks = computed(() => this.tasks().filter(t => t.done));
remainingTasks = computed(() => this.tasks().filter(t => !t.done));
}
<!-- app.component.html - using Angular control flow syntax with computed task lists -->
<h3>Completed</h3>
@for (task of completedTasks(); track task) {
<p>✅ {{ task.name }}</p>
}
<h3>Remaining</h3>
@for (task of remainingTasks(); track task) {
<p>❌ {{ task.name }}</p>
}
The tasks signal holds an array of task objects. Two computed signals are created:
completedTasks → tasks where done is true.remainingTasks → tasks where done is false.Whenever the tasks array changes (for example, when you mark a task as done or add a new one), both computed signals recompute, and the UI shows updated lists of completed and remaining tasks.
isFormValid computed from multiple field signals).price, quantity, or tasks), not the computed ones.firstName and lastName, and a computed signal fullName. Add buttons to change each name and observe how fullName updates.price and quantity).tasks signal with several items, then add computed signals for completed, remaining, and total counts of tasks.isCheckoutDisabled that depends on form field signals (for example, address, payment method) and use it to enable/disable a button.