{"id":4264,"date":"2026-03-19T11:18:51","date_gmt":"2026-03-19T10:18:51","guid":{"rendered":"https:\/\/nguenkam.com\/blog\/?p=4264"},"modified":"2026-03-19T11:18:51","modified_gmt":"2026-03-19T10:18:51","slug":"angular-signals-computed-vs-linkedsignal-when-to-use-which","status":"publish","type":"post","link":"https:\/\/nguenkam.com\/blog\/index.php\/2026\/03\/19\/angular-signals-computed-vs-linkedsignal-when-to-use-which\/","title":{"rendered":"Angular Signals: computed() vs linkedSignal() \u2014 When to Use Which"},"content":{"rendered":"\n<p>Angular Signals make reactive state much easier to reason about.<br>Still, many developers mix up two APIs: <strong><code>computed()<\/code><\/strong> and <strong><code>linkedSignal()<\/code><\/strong>.<\/p>\n\n\n\n<p>They both react to change \u2014 but they solve <strong>different problems<\/strong>.<\/p>\n\n\n\n<p><\/p>\n\n\n\n<h4>1)&nbsp;<code>computed()<\/code>: derived,&nbsp;<strong>read-only<\/strong>&nbsp;values<\/h4>\n\n\n\n<p>Use <code>computed()<\/code> when a value is purely calculated from other signals (think Excel formula).<\/p>\n\n\n\n<p><strong>Key traits:<\/strong><\/p>\n\n\n\n<ul><li>automatically recalculates when dependencies change<\/li><li>cannot be manually set with&nbsp;<code>.set()<\/code><\/li><li>ideal for UI display values, totals, filters, and status labels<\/li><\/ul>\n\n\n\n<p>Example:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>import { signal, computed } from '@angular\/core';\r\n\r\nconst price = signal(120);\r\nconst vat = signal(0.19);\r\n\r\nconst grossPrice = computed(() => price() * (1 + vat()));\r\n\r\nconsole.log(grossPrice()); \/\/ 142.8\r\nprice.set(200);\r\nconsole.log(grossPrice()); \/\/ 238\r<\/code><\/pre>\n\n\n\n<h4>2)&nbsp;<code>linkedSignal()<\/code>: derived, but&nbsp;<strong>editable<\/strong><\/h4>\n\n\n\n<p>Use <code>linkedSignal()<\/code> when a value comes from a source signal but should still be editable locally (for example, form draft state).<\/p>\n\n\n\n<p>Typical scenario:<\/p>\n\n\n\n<ul><li>selected entity changes (e.g., another user)<\/li><li>input field should initialize from that source<\/li><li>user can edit locally<\/li><li>when source changes, local signal can resync meaningfully<\/li><\/ul>\n\n\n\n<h5>Practical Example: \u201cEdit Selected User\u201d<\/h5>\n\n\n\n<p>Imagine you have a selected user and a name input field.<\/p>\n\n\n\n<ul><li>With\u00a0<code>computed()<\/code>, the name is display-only.<\/li><li>With\u00a0<code>linkedSignal()<\/code>, you get an editable draft that can still follow source changes.<\/li><\/ul>\n\n\n\n<pre class=\"wp-block-code\"><code>import { signal, computed, linkedSignal } from '@angular\/core';\r\n\r\ntype User = { id: number; name: string };\r\n\r\nconst users = signal&lt;User&#91;]>(&#91;\r\n  { id: 1, name: 'Anna' },\r\n  { id: 2, name: 'Ben' },\r\n]);\r\n\r\nconst selectedUserId = signal(1);\r\n\r\nconst selectedUser = computed(() =>\r\n  users().find(u => u.id === selectedUserId()) ?? null\r\n);\r\n\r\n\/\/ Read-only derived display value\r\nconst upperCaseName = computed(() =>\r\n  selectedUser()?.name.toUpperCase() ?? ''\r\n);\r\n\r\n\/\/ Editable value linked to selectedUser\r\nconst editableName = linkedSignal&lt;string | null>({\r\n  source: selectedUser,\r\n  computation: (user) => user?.name ?? null\r\n});\r\n\r\n\/\/ --- Usage ---\r\n\/\/ initial: \"Anna\"\r\nconsole.log(editableName());\r\n\r\n\/\/ user types in an input:\r\neditableName.set('Anna (Draft)');\r\nconsole.log(editableName()); \/\/ \"Anna (Draft)\"\r\n\r\n\/\/ selection changes to Ben:\r\nselectedUserId.set(2);\r\n\r\n\/\/ linked signal can derive again from the new source\r\nconsole.log(editableName()); \/\/ typically \"Ben\"\r<\/code><\/pre>\n\n\n\n<blockquote class=\"wp-block-quote\"><p><span class=\"has-inline-color has-luminous-vivid-orange-color\"><em>Note: Exact resync behavior can vary slightly by Angular version\/options, but the core idea stays the same: <strong>locally editable + source-linked<\/strong>.<\/em><\/span><\/p><\/blockquote>\n\n\n\n<p><\/p>\n\n\n\n<h4>Why not just use\u00a0<code>computed()<\/code>\u00a0everywhere?<\/h4>\n\n\n\n<p>Because <code>computed()<\/code> is intentionally strict:<br>it guarantees pure derivation and prevents local mutation.<\/p>\n\n\n\n<p>That\u2019s excellent for consistency \u2014 but not for temporary UI edits where users type intermediate values.<\/p>\n\n\n\n<h5>Practical Rule of Thumb<\/h5>\n\n\n\n<ul><li>Use&nbsp;<strong><code>computed()<\/code><\/strong>&nbsp;for pure derived values.<\/li><li>Use&nbsp;<strong><code>linkedSignal()<\/code><\/strong>&nbsp;for source-initialized values that also need local edits.<\/li><\/ul>\n","protected":false},"excerpt":{"rendered":"<p>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 \u2014 but they solve different problems. 1)&nbsp;computed(): derived,&nbsp;read-only&nbsp;values Use computed() when a value is purely calculated from other signals (think Excel formula). Key traits: automatically recalculates when dependencies change cannot be [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":4242,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":[],"categories":[37],"tags":[1117,992,1131,735],"_links":{"self":[{"href":"https:\/\/nguenkam.com\/blog\/index.php\/wp-json\/wp\/v2\/posts\/4264"}],"collection":[{"href":"https:\/\/nguenkam.com\/blog\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/nguenkam.com\/blog\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/nguenkam.com\/blog\/index.php\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/nguenkam.com\/blog\/index.php\/wp-json\/wp\/v2\/comments?post=4264"}],"version-history":[{"count":1,"href":"https:\/\/nguenkam.com\/blog\/index.php\/wp-json\/wp\/v2\/posts\/4264\/revisions"}],"predecessor-version":[{"id":4265,"href":"https:\/\/nguenkam.com\/blog\/index.php\/wp-json\/wp\/v2\/posts\/4264\/revisions\/4265"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/nguenkam.com\/blog\/index.php\/wp-json\/wp\/v2\/media\/4242"}],"wp:attachment":[{"href":"https:\/\/nguenkam.com\/blog\/index.php\/wp-json\/wp\/v2\/media?parent=4264"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/nguenkam.com\/blog\/index.php\/wp-json\/wp\/v2\/categories?post=4264"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/nguenkam.com\/blog\/index.php\/wp-json\/wp\/v2\/tags?post=4264"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}