# LinkedSignal vs Computed: The Angular Signals Showdown You've Been Waiting For

### Introduction

Ever felt like Angular's change detection was that one friend who shows up uninvited to every party? You know, constantly checking if anything changed, even when nothing did? 🤔

Well, Angular Signals just changed the game—and if you're not using `computed` and `linkedSignal` yet, you're missing out on some serious performance gains and cleaner code.

**By the end of this article, you'll know:**

* When to reach for `computed` vs `linkedSignal` (and why it matters)
    
* How to build reactive features without Zone.js breathing down your neck
    
* Real-world patterns that'll make your senior devs do a double-take
    
* The performance tricks that separate good Angular apps from *great* ones
    

Let's dive in! But first, quick question: **Are you still manually managing subscriptions in 2025?** Drop a comment below—I'm genuinely curious! 👇

---

## Why Angular Signals Are Your New Best Friend

Remember the days of wrestling with RxJS subscriptions, memory leaks, and that one `ngOnDestroy` you forgot to implement? Angular Signals are here to save us from that chaos.

Signals bring **fine-grained reactivity** to Angular—meaning your app only updates what needs updating, when it needs updating. No more Zone.js checking everything under the sun.

```tsx
// The old way (we've all been there)
export class OldSchoolComponent {
  count$ = new BehaviorSubject(0);
  doubled$ = this.count$.pipe(map(n => n * 2));

  ngOnDestroy() {
    // Don't forget this! (But we always do...)
  }
}

// The signals way (clean and simple)
export class ModernComponent {
  count = signal(0);
  doubled = computed(() => this.count() * 2);
  // No cleanup needed! 🎉
}
```

---

## Computed Signals: Your Reactive Calculator 🧮

### What Are Computed Signals?

Think of `computed` as that smart friend who always knows the answer because they're constantly doing the math in their head. Computed signals **automatically derive values** from other signals and update whenever their dependencies change.

### The Magic in Action

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

export class PriceCalculatorComponent {
  // Base signals
  quantity = signal(1);
  pricePerUnit = signal(99.99);
  discountPercent = signal(0);

  // Computed magic happens here
  subtotal = computed(() =>
    this.quantity() * this.pricePerUnit()
  );

  discountAmount = computed(() =>
    this.subtotal() * (this.discountPercent() / 100)
  );

  total = computed(() =>
    this.subtotal() - this.discountAmount()
  );

  // Update quantity, everything recalculates automatically!
  addToCart() {
    this.quantity.update(q => q + 1);
  }
}
```

**When should you use computed?**

* ✅ Deriving values from other signals
    
* ✅ Calculations that depend on multiple signals
    
* ✅ Read-only derived state
    
* ✅ Performance-critical computations (they're memoized!)
    

💡 **Pro tip:** Computed signals are **lazy** and **cached**. They only recalculate when accessed AND their dependencies changed. That's free performance right there!

---

## LinkedSignal: The Two-Way Street 🔄

### Enter LinkedSignal: Computed's Flexible Cousin

While `computed` is read-only, `linkedSignal` is that friend who listens but also has opinions. It can derive from other signals AND be manually updated—perfect for syncing with external sources or handling bi-directional data flow.

### LinkedSignal in the Wild

```tsx
import { signal, linkedSignal } from '@angular/core';

export class UserPreferencesComponent {
  // Source signal
  fahrenheit = signal(72);

  // LinkedSignal that syncs both ways
  celsius = linkedSignal(() =>
    Math.round((this.fahrenheit() - 32) * 5/9)
  );

  // You can update it directly!
  updateCelsius(value: number) {
    this.celsius.set(value);
    // Now fahrenheit is out of sync, but that's okay!
    // LinkedSignal allows this flexibility
  }

  // Or sync it back
  syncFromCelsius() {
    const c = this.celsius();
    this.fahrenheit.set(Math.round(c * 9/5 + 32));
  }
}
```

### Real-World Example: Form Input Sync

Here's where `linkedSignal` really shines—syncing form inputs with formatted displays:

```tsx
export class PaymentFormComponent {
  // Raw input value
  rawCardNumber = signal('');

  // Formatted display that can also be edited
  formattedCardNumber = linkedSignal(() => {
    const raw = this.rawCardNumber().replace(/\\s/g, '');
    return raw.match(/.{1,4}/g)?.join(' ') || '';
  });

  onFormattedInput(value: string) {
    // Update the formatted version
    this.formattedCardNumber.set(value);
    // Extract and update raw
    this.rawCardNumber.set(value.replace(/\\s/g, ''));
  }
}
```

**When to reach for linkedSignal?**

* ✅ Two-way data binding scenarios
    
* ✅ Syncing with external APIs or localStorage
    
* ✅ Form inputs that need formatting
    
* ✅ Temporary overrides of computed values
    

---

## The Ultimate Comparison: Computed vs LinkedSignal 🥊

### What They Share

* 🎯 Both derive from other signals
    
* 🎯 Both update reactively
    
* 🎯 Both are lazy (compute on-demand)
    
* 🎯 Both integrate seamlessly with Angular's template system
    

### The Key Differences

| Feature | Computed | LinkedSignal |
| --- | --- | --- |
| **Mutability** | Read-only | Read-write |
| **Use Case** | Pure derivations | Bi-directional sync |
| **Performance** | Highly optimized, cached | Flexible but less cached |
| **Best For** | Calculations, transformations | Forms, external sync |
| **Can be set()** | ❌ Never | ✅ Yes |
| **Stays in sync** | ✅ Always | 🔄 Until manually changed |

### Decision Tree (Yes, I Made One for You!)

```tsx
// Ask yourself:
const shouldUseComputed = () => {
  if (needToManuallyUpdate) return false;
  if (pureCalculation) return true;
  if (externalDataSync) return false;
  return true; // When in doubt, computed is usually right
};
```

**Quick question:** What's your most complex reactive state scenario? I'd love to hear how you'd solve it with signals! Drop it in the comments 💬

---

## Real-World Example: Dynamic Pricing Calculator 💰

Let's build something practical—a pricing calculator with tax, discounts, and currency conversion:

```tsx
@Component({
  selector: 'app-pricing',
  template: `
    <div class="pricing-calculator">
      <h3>Product Pricing</h3>

      <label>
        Quantity:
        <input type="number"
               [value]="quantity()"
               (input)="quantity.set(+$event.target.value)">
      </label>

      <label>
        Discount Code:
        <input [value]="discountCode()"
               (input)="applyDiscount($event.target.value)">
      </label>

      <div class="results">
        <p>Subtotal: {{ subtotal() | currency }}</p>
        <p>Discount: -{{ discountAmount() | currency }}</p>
        <p>Tax: {{ taxAmount() | currency }}</p>
        <h4>Total: {{ finalPrice() | currency }}</h4>

        <!-- LinkedSignal for currency display -->
        <p>In EUR: € {{ priceInEur() }}</p>
        <button (click)="overrideEurPrice()">
          Set Custom EUR Price
        </button>
      </div>
    </div>
  `
})
export class PricingCalculatorComponent {
  // Base signals
  quantity = signal(1);
  unitPrice = signal(49.99);
  discountCode = signal('');
  taxRate = signal(0.08); // 8% tax

  // Computed for calculations
  subtotal = computed(() =>
    this.quantity() * this.unitPrice()
  );

  discountPercent = computed(() => {
    const code = this.discountCode();
    // Simple discount logic
    switch(code) {
      case 'SAVE10': return 0.10;
      case 'SAVE20': return 0.20;
      case 'HALFOFF': return 0.50;
      default: return 0;
    }
  });

  discountAmount = computed(() =>
    this.subtotal() * this.discountPercent()
  );

  taxableAmount = computed(() =>
    this.subtotal() - this.discountAmount()
  );

  taxAmount = computed(() =>
    this.taxableAmount() * this.taxRate()
  );

  finalPrice = computed(() =>
    this.taxableAmount() + this.taxAmount()
  );

  // LinkedSignal for currency conversion
  // Can be overridden by user or API
  priceInEur = linkedSignal(() =>
    // Assuming 1 USD = 0.92 EUR
    Math.round(this.finalPrice() * 0.92 * 100) / 100
  );

  applyDiscount(code: string) {
    this.discountCode.set(code.toUpperCase());
  }

  overrideEurPrice() {
    // Maybe from an API or user input
    const customPrice = prompt('Enter custom EUR price:');
    if (customPrice) {
      this.priceInEur.set(parseFloat(customPrice));
    }
  }
}
```

---

## Unit Testing Your Signals (Because We're Professionals) 🧪

Nobody talks about testing signals, but here's how to do it right:

```tsx
describe('PricingCalculatorComponent', () => {
  let component: PricingCalculatorComponent;

  beforeEach(() => {
    TestBed.configureTestingModule({
      imports: [PricingCalculatorComponent]
    });

    const fixture = TestBed.createComponent(PricingCalculatorComponent);
    component = fixture.componentInstance;
  });

  it('should calculate subtotal correctly', () => {
    component.quantity.set(3);
    component.unitPrice.set(10);

    expect(component.subtotal()).toBe(30);
  });

  it('should apply discount code', () => {
    component.quantity.set(2);
    component.unitPrice.set(50);
    component.discountCode.set('SAVE20');

    expect(component.discountAmount()).toBe(20);
    expect(component.finalPrice()).toBe(86.4); // 80 + 8% tax
  });

  it('should allow EUR price override with linkedSignal', () => {
    component.finalPrice = signal(100); // Mock final price

    // Check computed value
    expect(component.priceInEur()).toBeCloseTo(92);

    // Override it
    component.priceInEur.set(95);
    expect(component.priceInEur()).toBe(95);

    // It's now disconnected from the source
    component.finalPrice.set(200);
    expect(component.priceInEur()).toBe(95); // Still 95!
  });
});
```

---

## Best Practices & Performance Tips 🚀

### Do's ✅

1. **Keep computed signals pure**
    
    ```tsx
    // Good
    total = computed(() => this.price() * this.quantity());
    
    // Bad - side effects!
    total = computed(() => {
      console.log('Computing...'); // Don't do this
      return this.price() * this.quantity();
    });
    ```
    
2. **Use linkedSignal for temporary overrides**
    
    ```tsx
    // Perfect use case
    autoSavedValue = linkedSignal(() => this.userInput());
    // User can override, but it resyncs on source change
    ```
    
3. **Batch signal updates**
    
    ```tsx
    updateMultiple() {
      // Angular batches these automatically!
      this.firstName.set('John');
      this.lastName.set('Doe');
      this.age.set(30);
      // Only one change detection cycle!
    }
    ```
    

### Don'ts ❌

1. **Don't create circular dependencies**
    
    ```tsx
    // This will cause infinite loops!
    a = computed(() => this.b() + 1);
    b = computed(() => this.a() - 1);
    ```
    
2. **Don't overuse linkedSignal**
    
    ```tsx
    // If you never need to set(), use computed instead
    readonly = linkedSignal(() => this.source());
    // Better: readonly = computed(() => this.source());
    ```
    

### 💡 **Bonus Tip: The Signal Effect Pattern**

Here's a pattern I love for side effects with signals:

```tsx
export class NotificationComponent {
  message = signal('');

  constructor() {
    // React to signal changes with effects
    effect(() => {
      const msg = this.message();
      if (msg) {
        this.showToast(msg);
        // Auto-clear after 3 seconds
        setTimeout(() => this.message.set(''), 3000);
      }
    });
  }

  private showToast(msg: string) {
    // Your toast logic here
  }
}
```

---

## Recap: Your Signal Superpowers 🦸‍♂️

Let's wrap this up with what you've learned:

🎯 **Computed signals** are your go-to for:

* Derived values that are always in sync
    
* Performance-critical calculations
    
* Read-only reactive state
    

🎯 **LinkedSignals** shine when you need:

* Bi-directional data flow
    
* Manual overrides of computed values
    
* Syncing with external sources
    

🎯 **Key takeaway:** Start with `computed`. Only reach for `linkedSignal` when you actually need that write capability. Your future self (and your team) will thank you.

---

## Let's Keep This Conversation Going! 🚀

### 💭 **What did you think?**

Did this clear up the computed vs linkedSignal confusion? What's your take on Angular's new reactive model? Drop a comment below—I read every single one and love the discussions that follow!

### 👏 **Found this helpful?**

If this saved you from a signals-induced headache (or taught you something new), smash that clap button! Seriously, even one clap makes my day—and helps other devs discover this content.

### 🎯 **Your Action Items:**

1. **Try this out:** Refactor one component to use signals this week
    
2. **Spread the knowledge:** Share this with that one colleague still using RxJS for everything
    

**One last question before you go:**

What Angular topic should I tackle next?

A) Signal-based state management patterns

B) Standalone components deep dive

C) Performance profiling in Angular 18+

Vote in the comments! 👇

---

### 🚀 Follow Me for More Angular & Frontend Goodness:

I regularly share hands-on tutorials, clean code tips, scalable frontend architecture, and real-world problem-solving guides.

* 💼 [**LinkedIn**](https://www.linkedin.com/in/errajatmalik/) — Let’s connect professionally
    
* 🎥 [**Threads**](https://www.threads.net/@er.rajatmalik) — Short-form frontend insights
    
* 🐦 [**X (Twitter)**](https://twitter.com/er_rajatmalik) — Developer banter + code snippets
    
* 👥 [**BlueSky**](http://devrajat.bsky.social/) — Stay up to date on frontend trends
    
* 🌟 [**GitHub Projects**](https://github.com/malikrajat) — Explore code in action
    
* 🌐 [**Website**](https://malikrajat.github.io/) — Everything in one place
    
* 📚 [**Medium Blog**](https://medium.com/@codewithrajat/) — Long-form content and deep-dives
    
* 💬 [**Dev Blog**](https://dev.to/codewithrajat) — Free Long-form content and deep-dives
    
* ✉️ [**Substack**](https://codewithrajat.substack.com/) — Weekly frontend stories & curated resources
    
* 🧩 [**Portfolio**](https://rajatmalik.dev/) — Projects, talks, and recognitions
    
* ✍️ [**Hashnode**](https://hashnode.com/@codeswithrajat) — Developer blog posts & tech discussions
    

---

### 🎉 If you found this article valuable:

* Leave a **👏 Clap**
    
* Drop a **💬 Comment**
    
* Hit **🔔 Follow** for more weekly frontend insights
    

Let’s build cleaner, faster, and smarter web apps — **together**.

**Stay tuned for more Angular tips, patterns, and performance tricks!** 🧪🧠🚀

[✨ Share Your Thoughts To 📣 Set Your Notification Preference](https://forms.gle/mdfXEVh3AxYwAsKw5)
