๐ Angular's `linkedSignal` and `resource()`: A Practical Guide for 2026
Hi ๐, I'm Tushar Patil. Currently I am working as Frontend Developer (Angular) and also have expertise with .Net Core and Framework.
Search for a command to run...
Hi ๐, I'm Tushar Patil. Currently I am working as Frontend Developer (Angular) and also have expertise with .Net Core and Framework.
No comments yet. Be the first to comment.
**No credit card. No trial tricks. Real inference.** A comprehensive breakdown of every major AI provider offering genuinely free API access โ with exact rate limits, available models, and when each one makes sense for your project.
One agent cannot do everything well โ build an orchestrator that decomposes tasks, spawns specialist sub-agents, runs them in parallel, and synthesises their results into a single coherent answer
When multiple AI agents share an MCP server, make every tool call, session event, and error visible in real time โ broadcast them over SSE and render a live dashboard that shows exactly what your agents are doing
Stop guessing whether your RAG agent is getting better or worse โ build a systematic eval framework that scores faithfulness, relevance, and precision, then gate your CI pipeline on it
When Tenant A searches your knowledge base, they must never see Tenant B's documents โ enforce this at the database level, not in application code
Angular's Signals story has been building for three years. With Angular 20, the final pieces clicked into place: linkedSignal() and resource() both graduated to stable, and together they solve two problems that every Angular developer hits repeatedly.
The first problem is dependent writable state โ you need a signal whose initial value is derived from another signal, but the user can also change it directly. computed() is read-only, so it's no help. Before linkedSignal(), developers reached for awkward effect() workarounds or manual synchronization logic. ๐ฉ
The second problem is async data in a signals world โ you have a reactive fetch that should re-run automatically when params change, surface loading/error states as signals, and cancel in-flight requests when the params change again. Previously this meant wiring up RxJS observables, managing subscriptions, and handling race conditions by hand. resource() handles all of that declaratively. ๐
Let's look at each one in depth.
linkedSignal()Suppose you have a shipping options component. The available options come from a server (or a parent component as an input), and you want to pre-select the first one. But the user can also pick a different option themselves.
Before linkedSignal(), the typical approach was to use a signal() for the selection and an effect() to reset it when the options changed. This works, but effect() is meant for side effects โ using it for state derivation is a code smell that the Angular team actively warns against. ๐ฉ
linkedSignal() is the right tool: it creates a writable signal whose value resets automatically when its reactive source changes. โจ
import { Component, Signal, linkedSignal } from '@angular/core';
@Component({ /* ... */ })
export class ShippingPickerComponent {
shippingOptions: Signal<string[]> = getShippingOptions();
// โ
Automatically initializes to the first option.
// If shippingOptions changes (e.g., user switches region),
// selectedOption resets to the new first element.
selectedOption = linkedSignal(() => this.shippingOptions()[0]);
selectOption(index: number) {
this.selectedOption.set(this.shippingOptions()[index]);
}
}
The key difference from computed(): selectedOption is a WritableSignal. The user can call .set() on it, and that change holds โ until shippingOptions changes, at which point the computation re-runs and resets it to the new first element. ๐
Sometimes you don't want a full reset. Say the user has selected "Air" shipping โ๏ธ, and then the available options refresh with a new list that still includes "Air". Resetting to the first item would be a bad UX โ you should keep their selection if it's still valid.
The advanced linkedSignal() form gives you access to the previous value:
interface ShippingMethod {
id: number;
name: string;
}
selectedOption = linkedSignal<ShippingMethod[], ShippingMethod>({
source: () => this.shippingOptions(),
computation: (newOptions, previous) => {
// ๐ If the user had a selection and it still exists in the new list, keep it
if (previous) {
const stillAvailable = newOptions.find(
opt => opt.id === previous.value.id
);
if (stillAvailable) return stillAvailable;
}
// Otherwise default to the first option
return newOptions[0];
}
});
The computation function receives:
source โ the new value of the source signalprevious โ an object with previous.source (old source value) and previous.value (the previous linkedSignal value)๐ Note: When using the
previousparameter, you must provide generic type arguments explicitly:linkedSignal<SourceType, ValueType>.
linkedSignal() for editable form copiesThis is the most common real-world use case. You load data from the server, display it in a form, and let the user edit it. When the user saves, you send the edited copy. If the source data reloads (e.g., another user changed it), the form should refresh. ๐
@Component({ /* ... */ })
export class UserProfileComponent {
loadedUser = this.store.user; // a Signal<User>
// โ๏ธ Editable local copies โ reset if loadedUser changes
name = linkedSignal(() => this.loadedUser().name);
email = linkedSignal(() => this.loadedUser().email);
role = linkedSignal(() => this.loadedUser().role);
save() {
this.store.update({
...this.loadedUser(),
name: this.name(),
email: this.email(),
role: this.role(),
});
}
}
The template uses two-way binding directly on the linked signals:
<input [(ngModel)]="name" />
<input [(ngModel)]="email" />
<select [(ngModel)]="role"> ... </select>
<button (click)="save()">Save</button>
The user can freely edit the form. If the server pushes an update to loadedUser, all three fields reset to reflect the new data. Clean, and zero effect() calls. ๐ช
linkedSignal()Use computed() when the derived value should be strictly read-only โ for example, a total price derived from quantity and unit price. There's no scenario where you'd want the user to override that value directly. linkedSignal() is specifically for cases where manual overrides are part of the intended UX.
resource()Before resource(), fetching data reactively in Angular typically meant:
HttpClientswitchMap in an RxJS pipeline to react to param changesswitchMap)That's a lot of boilerplate for something as universal as "fetch data when this signal changes." resource() replaces the whole pattern. ๐
resource() worksA resource has two parts:
params ๐๏ธ โ a reactive function (like computed()) that returns the parameters for the async operation. When the params change, the resource automatically re-fetches.loader ๐ โ an async function that receives the current params and returns a Promise.import { resource, signal } from '@angular/core';
userId = signal<string>('user-1');
userResource = resource({
params: () => ({ id: this.userId() }),
loader: ({ params }) => fetch(`/api/users/${params.id}`)
.then(res => res.json())
});
The resource automatically exposes the following signals:
| Signal | Type | Description |
|---|---|---|
.value() โ
|
T | undefined |
The resolved data |
.isLoading() โณ |
boolean |
True while the loader is running |
.error() โ |
unknown |
The thrown error, if any |
.hasValue() ๐ |
boolean |
True when data is available |
.status() ๐ |
ResourceStatus |
Full status string |
@if (userResource.isLoading()) {
<p>โณ Loading...</p>
} @else if (userResource.error()) {
<p>โ Error: could not load user.</p>
} @else if (userResource.hasValue()) {
<user-profile [user]="userResource.value()" />
}
โ ๏ธ Always guard
.value()withhasValue()โ reading.value()when the resource is in an error state throws at runtime.
rxResource() โ the RxJS-friendly variantIf your data layer uses HttpClient (which returns Observable), use rxResource() from @angular/core/rxjs-interop. It's identical to resource() except the loader function returns an Observable instead of a Promise. As of Angular 20, the loader key is called stream in rxResource().
import { inject, signal } from '@angular/core';
import { rxResource } from '@angular/core/rxjs-interop';
import { HttpClient } from '@angular/common/http';
@Component({ /* ... */ })
export class ProductListComponent {
private http = inject(HttpClient);
page = signal(1);
productsResource = rxResource({
params: () => this.page(),
stream: ({ params: page }) =>
this.http.get<Product[]>(`/api/products?page=${page}`)
});
}
rxResource() automatically unsubscribes and cancels in-flight requests when params change โ no switchMap, no takeUntilDestroyed. ๐ง
httpResource() โ the simplest option for plain HTTP GETFor the most common case โ a reactive HTTP GET with a dynamic URL โ httpResource() is the most concise API:
import { httpResource } from '@angular/core';
@Component({ /* ... */ })
export class UserComponent {
userId = input.required<string>();
// ๐ Automatically re-fetches whenever userId changes
user = httpResource(() => `/api/users/${this.userId()}`);
}
httpResource() runs through Angular's HttpClient stack, including interceptors, and returns the response as signals. It also supports response type variants (.text(), .blob(), .arrayBuffer()) and schema validation via Zod or Valibot via a parse option.
๐งช Note:
httpResource()is still experimental as of Angular 21.resource()andrxResource()are stable.
resource() with linkedSignal()This is where the two APIs shine together. โจ Say you fetch a list of posts and want to track the selected post โ defaulting to the first one, but letting the user pick:
@Component({ /* ... */ })
export class PostBrowserComponent {
private http = inject(HttpClient);
category = signal('angular');
postsResource = rxResource({
params: () => this.category(),
stream: ({ params: cat }) =>
this.http.get<Post[]>(`/api/posts?category=${cat}`)
});
// ๐ฏ Defaults to first post, resets when posts reload,
// but user can select any post manually
selectedPost = linkedSignal(
() => this.postsResource.value()?.[0] ?? null
);
selectPost(post: Post) {
this.selectedPost.set(post);
}
}
When category changes, postsResource re-fetches, and selectedPost automatically resets to the new first post. The user can select any post in between, and that selection holds until the next category change. ๐
One of the most important things resource() does behind the scenes: if params change while a load is already in flight, the previous request is aborted (for resource() via AbortSignal, and for rxResource() via automatic unsubscription). You never need to think about stale responses overwriting fresh ones. ๐ก๏ธ
Resources can depend on other resources via computed():
userResource = resource({
params: () => ({ id: this.userId() }),
loader: ({ params }) => fetchUser(params)
});
// โ๏ธ Only triggers when userResource has a value
profilePicResource = httpResource(
() => this.userResource.hasValue()
? `/api/images/${this.userResource.value()!.profilePicId}`
: undefined
);
When the params function returns undefined, the resource stays in idle state and doesn't fire a request. ๐ด
Resources expose a .reload() method for cases like "pull to refresh":
<button (click)="postsResource.reload()">๐ Refresh</button>
resource() for mutationsresource() is designed for read operations only. If params change while a POST or PUT is in flight, the request gets cancelled โ meaning your mutation might never reach the server. โ ๏ธ Use HttpClient directly (or a dedicated mutations pattern) for writes.
| Scenario | Use |
|---|---|
| Derive a value reactively, user can override it ๐๏ธ | linkedSignal() |
| Editable form copy synced with server data ๐ | linkedSignal() |
| Fetch data reactively using Promise ๐ค | resource() |
| Fetch data reactively using HttpClient/Observable ๐ | rxResource() |
| Simple reactive HTTP GET, don't need full control โก | httpResource() |
| Strict read-only derived value ๐ | computed() |
| Side effects (logging, DOM, non-Angular APIs) โ๏ธ | effect() |