Angular Signals make reactive state much easier to reason about.
Still, many developers mix up two APIs: computed() and linkedSignal().
They both react to change — but they solve different problems.
1) computed(): derived, read-only values
Use computed() when a value is purely calculated from other signals (think Excel formula).
Key traits:
- automatically recalculates when dependencies change
- cannot be manually set with
.set() - ideal for UI display values, totals, filters, and status labels
Example:
import { signal, computed } from '@angular/core';
const price = signal(120);
const vat = signal(0.19);
const grossPrice = computed(() => price() * (1 + vat()));
console.log(grossPrice()); // 142.8
price.set(200);
console.log(grossPrice()); // 238
2) linkedSignal(): derived, but editable
Use linkedSignal() when a value comes from a source signal but should still be editable locally (for example, form draft state).
Typical scenario:
- selected entity changes (e.g., another user)
- input field should initialize from that source
- user can edit locally
- when source changes, local signal can resync meaningfully
Practical Example: “Edit Selected User”
Imagine you have a selected user and a name input field.
- With
computed(), the name is display-only. - With
linkedSignal(), you get an editable draft that can still follow source changes.
import { signal, computed, linkedSignal } from '@angular/core';
type User = { id: number; name: string };
const users = signal<User[]>([
{ id: 1, name: 'Anna' },
{ id: 2, name: 'Ben' },
]);
const selectedUserId = signal(1);
const selectedUser = computed(() =>
users().find(u => u.id === selectedUserId()) ?? null
);
// Read-only derived display value
const upperCaseName = computed(() =>
selectedUser()?.name.toUpperCase() ?? ''
);
// Editable value linked to selectedUser
const editableName = linkedSignal<string | null>({
source: selectedUser,
computation: (user) => user?.name ?? null
});
// --- Usage ---
// initial: "Anna"
console.log(editableName());
// user types in an input:
editableName.set('Anna (Draft)');
console.log(editableName()); // "Anna (Draft)"
// selection changes to Ben:
selectedUserId.set(2);
// linked signal can derive again from the new source
console.log(editableName()); // typically "Ben"
Note: Exact resync behavior can vary slightly by Angular version/options, but the core idea stays the same: locally editable + source-linked.
Why not just use computed() everywhere?
Because computed() is intentionally strict:
it guarantees pure derivation and prevents local mutation.
That’s excellent for consistency — but not for temporary UI edits where users type intermediate values.
Practical Rule of Thumb
- Use
computed()for pure derived values. - Use
linkedSignal()for source-initialized values that also need local edits.
