DEV Community

Cover image for LinkedSignal vs Computed: The Angular Signals Showdown You've Been Waiting For
Rajat
Rajat

Posted on

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

Master the Art of Reactive State Management in Angular 18+ Without Breaking a Sweat

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.

// 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! ๐ŸŽ‰
}

Enter fullscreen mode Exit fullscreen mode

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

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);
  }
}

Enter fullscreen mode Exit fullscreen mode

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

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));
  }
}

Enter fullscreen mode Exit fullscreen mode

Real-World Example: Form Input Sync

Here's where linkedSignal really shinesโ€”syncing form inputs with formatted displays:

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, ''));
  }
}

Enter fullscreen mode Exit fullscreen mode

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!)

// 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
};

Enter fullscreen mode Exit fullscreen mode

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:

@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));
    }
  }
}

Enter fullscreen mode Exit fullscreen mode

Unit Testing Your Signals (Because We're Professionals) ๐Ÿงช

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

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!
  });
});

Enter fullscreen mode Exit fullscreen mode

Best Practices & Performance Tips ๐Ÿš€

Do's โœ…

  1. Keep computed signals pure

    // 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

    // Perfect use case
    autoSavedValue = linkedSignal(() => this.userInput());
    // User can override, but it resyncs on source change
    
    
  3. Batch signal updates

    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

    // This will cause infinite loops!
    a = computed(() => this.b() + 1);
    b = computed(() => this.a() - 1);
    
    
  2. Don't overuse linkedSignal

    // 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:

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
  }
}

Enter fullscreen mode Exit fullscreen mode

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 โ€” Letโ€™s connect professionally
  • ๐ŸŽฅ Threads โ€” Short-form frontend insights
  • ๐Ÿฆ X (Twitter) โ€” Developer banter + code snippets
  • ๐Ÿ‘ฅ BlueSky โ€” Stay up to date on frontend trends
  • ๐ŸŒŸ GitHub Projects โ€” Explore code in action
  • ๐ŸŒ Website โ€” Everything in one place
  • ๐Ÿ“š Medium Blog โ€” Long-form content and deep-dives
  • ๐Ÿ’ฌ Dev Blog โ€” Free Long-form content and deep-dives
  • โœ‰๏ธ Substack โ€” Weekly frontend stories & curated resources
  • ๐Ÿงฉ Portfolio โ€” Projects, talks, and recognitions
  • โœ๏ธ Hashnode โ€” 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

Top comments (0)