The Angular signal-based reactivity system provides powerful primitives like
signal, computed, and effect to manage state reactively.

But in certain edge cases, something seemed to be missing ... until now!

Before linked signals, there used to be certain edge cases where there just wasn't a great way to implement those scenarios using a signal-based approach.

It's not that we couldn't make it work with signals for those scenarios.

But the signal-based code for those specific scenarios was just a bit awkward and verbose, and it just didn't feel right.

The good news is that linked signals solve many of these edge cases in a much more elegant way.

In this post, we’ll explore in detail what a linked signal is, when to use it and why.

Table of Contents

This post covers the following topics:

  • What is an Angular Linked Signal?
  • Key Features of linkedSignal
  • What does a linked signal look like?
  • When is a linked signal useful?
  • How to implement a linked signal
  • So why not just use linkedSignal everywhere, instead of computed?
  • Common examples of when to use linked signals
  • Conclusion

Notice that linked signals are strongly related to the other Angular signal primitives: signal, computed, and effect.

So if you haven't done so already, I recommend that you read first this other guide: Angular Signals: The Complete Guide.

What is an Angular Linked Signal?

A linkedSignal is a new primitive in Angular’s signal API that allows a writable signal to be linked to one or more source signals.

Unlike a computed signal, which is read-only, a linkedSignal maintains a reactive relationship with its sources while still being writable.

Key Features of linkedSignal:

Here are the key features of linkedSignal:

  • Writable: Unlike computed, which is read-only, a linkedSignal allows direct updates via the set or update methods.
  • Reactive: Exactly like computed, it derives its value from one or more source signals.

Let's have a look at some code to understand how it works.

What does a linked signal look like?

Here is a simple example:

const quantity = linkedSignal({
  source: () => ({ courseCode: this.selectedCourse() }),
  compute: ({ courseCode }, previous) => {
    const course = 
    this.courses.find(c => c.code === courseCode);
    return 
      course ? course.defaultQuantity : previous ?? 1;
  }
});

The key elements to understand linked signals are:

  • source: The source signal that the linked signal derives its value from.
  • compute: The function that computes the value of the linked signal.

So quantity is a linked signal that derives its value from the selectedCourse source signal.

Here is what is going on in more detail:

  • The source function defines the source signals that the linked signal is linked to.
  • The compute function is called whenever any of the source signals emit a new value.
  • compute receives the latest value of the source signals and the previous value of the linked signal.
  • It then returns the new value of the linked signal, which get's emitted to the consumers of the linked signal.

So in this case, the linked signal quantity will be updated whenever the selectedCourse signal changes.

So it really sounds just like a computed signal, right?

The difference is that, unlike computed, a linked signal is writable.

Unlike computed, you can update the value of the linked signal using the set or update methods like so:

quantity.set(20);
quantity.update(current => current + 1);

So as we we can see, linkedSignal is kind of a combination between the computed and signal primitives, all mixed into one.

So that's essentially what it is, but I bet you are still wondering why it's needed and why it's useful.

When is a linked signal useful?

To understand when a linked signal is useful, let's use a simple but still realistic example.

Let's say that we have a shopping cart, and that the user can add items to the cart and select the quantity of each item.

Each item on the list has a default quantity.

So if the user changes the selected product to add to the cart, the quantity should be reset to the default quantity of the selected product.

Here is what the html of the shopping cart component might look like:

<div class="demo-container">

  <h1>Shopping Cart</h1>

  <div class="form-control">

    <label>Select Course</label>

    <select [value]="selectedCourse()" 
    (change)="onCourseSelected(course.value)" #course>
      @for (course of courses; track course.code) {
        <option [value]="course.code">
            {{course.title}}
        </option>
      }
    </select>

  </div>

  <div class="form-control">

    <label>Quantity</label>

    <input type="number" 
       [value]="quantity()" 
       (change)="onQuantityChanged(input.value)"
       #input/>

  </div>

  <div class="form-actions">

    <button class="btn" 
            (click)="onArticleAdded()">
      Add To Cart
    </button>

  </div>

</div>

So let's break down what is going on here:

  • everything is done with signals: the selected course is a signal, the quantity is a signal, and the courses list is also a signal.
  • We have a list of courses, and the user can select one of them to add to the cart.
  • The quantity of the item to add to the cart is displayed in the quantity input field.
  • The quantity input field is linked to the selected course.
  • When the user changes the selected course, the quantity input field is reset to the default quantity of the selected course.

The quantity signal is the key part of this example.

It needs to be reset to the default quantity of the selected course, every time the user chooses a new course from the dropdown.

But it also needs to be writable, so the user can change the quantity manually.

So how do we implement this scenario?

Well, we could use a computed signal to compute the quantity based on the selected course.

But a computed signal is read-only, so we wouldn't be able to reset the quantity to the default quantity of the selected course when the user changes the selected course.

Alternatively, we could use an effect to update the quantity whenever the selected course changes.

that would look like this:

effect(() => {
  const selectedCourse = this.selectedCourse();
  quantity.set(
      courses.find(
      c => c.code === 
      selectedCourse)?.defaultQuantity ?? 1);
});

So it works, right? Well kind of.

Why not just use an effect?

This could work, but it feels a bit awkward.

It's just not very declarative, as we are writing the value imperatively of the quantity signal inside the effect.

Writing signal values inside effects is now possible, but it's still not a good idea, as it could easily create infinite loops.

It's not very obvious either that the quantity signal is linked to the selected course signal,just by looking at the signal declarations.

It's not that it can't be made to work, but it just feels wrong.

Effects are meant to be used for pure side effects, and not for this kind of reactive value computation where we are linking signals together.

Also, in general it's a good idea to avoid effects as much as possible, and only use them as a last resort.

So what's the alternative?

Well, that's where linkedSignal comes in.

Here is the complete component, written with a linked signal:

@Component({
  selector: 'linked-signal-demo',
  templateUrl: './linked-signal-demo.component.html',
})
export class LinkedSignalDemoComponent {

  // these are the products of the shopping cart 
  courses = [
    {
      code: "BEGINNERS",
      title: "Angular for Beginners",
      defaultQuantity: 10
    },
    {
      code: "SIGNALS",
      title: "Angular Signals In Depth",
      defaultQuantity: 20
    },
    {
      code: "SSR",
      title: "Angular SSR In Depth",
      defaultQuantity: 30
    }
  ];

  // this is the product that the user has 
  // selected to add to the cart 
  selectedCourse = 
  signal<string | null>("BEGINNERS");

  // this is the quantity of the product that the user 
  // has selected to add to the cart 
  // it needs to be reset to the default quantity 
  // of the last selected product, 
  // when the user changes the selected product
  quantity = linkedSignal({
    source: () => ({courseCode: this.selectedCourse}),
    computation: (source, previous) => {
      return 
      this.courses.find(c => c.code 
      === source.courseCode())?.defaultQuantity ?? 1
    }
  });

  onQuantityChanged(quantity: string) {
    this.quantity.set(parseInt(quantity));
  }

  onCourseSelected(courseCode: string) {
    this.selectedCourse.set(courseCode);
  }
}

So as you can see, the linkedSignal() solves this scenario in a much more elegant and declarative way.

The dependency between the selectedCourse and quantity signals is now made very explicit, we can see it just by looking at the signal declarations, which is great.

And the quantity signal is still writable, so the user can change the quantity manually.

If the user changes the selected course, the quantity will be reset to the default quantity of that same selected course.

Exactly what we wanted to achieve, and no effects were needed!

So why not just use linkedSignal everywhere, instead of computed?

Well, that's a good question.

The answer is that linked signals are not meant to replace computed signals, plain signals, or even effects.

They are a new primitive that fills a specific gap in the signal API.

They are not meant to be used everywhere, but only in very specific and relatively rare scenarios where you need both reactivity and the ability to change a value at the same time.

If you can solve a problem with a computed signal, then there is no need to use a linkedSignal.

If you just need a writeable signal, you should use just a plain signal, which is writeable by default.

Linked signals should only be needed every so often.

In most cases, the signal and computed primitives are more than enough to solve the vast majority of problems, and they should be our go-to default.

Common examples of when to use linked signals

Here are some common examples that you might run into where linked signals could be useful:

  • Form field resets based on another field’s value.
  • State management scenarios where values need to reset reactively but still allow user overrides.
  • If you are in a situation where computed signals are too restrictive, but effects feel too imperative, a linkedSignal might work better.

Conclusion

The linkedSignal primitive fills an important gap in Angular’s signal-based reactivity model.

It allows developers to define reactive yet writable relationships between signals, making it perfect for cases like field resets and other scenarios.

While it’s not meant to replace computed or signal at all, it provides a powerful alternative when writable computed values are needed.

If you’re already using Angular’s signal API, give linkedSignal a go — it will help make your code cleaner and more maintainable.

But still try to solve most problems with the computed and signal primitives, as they are more than enough for most scenarios.

For more in-depth signals content, check out my Modern Angular with Signals Course at the Angular University. 🚀