
Intermediate workshop
RxJS Masterclass
Learn RxJS from the basics to mastery. Understand everything: from core concepts and simple operators all the way to best practices, custom operators and marble testing.
Learn more
In v19, Angular will introduce a new API for loading resources. This would allow us to fetch data from an api, know about the status of the request, and update the data locally when needed.
The resource API is very simple in it's core. Let's see the most simple example of how to use it.
import { resource } from "@angular/core";
@Component({})
export class MyComponent {
todoResource = resource({
loader: () => {
return Promise.resolve({ id: 1, title: "Hello World", completed: false });
},
});
constructor() {
effect(() => {
console.log("Value: ", this.todoResource.value());
console.log("Status: ", this.todoResource.status());
console.log("Error: ", this.todoResource.error());
})
}
}
The first thing we can notice is that the resource API uses Promises by default for the loader parameter. The other one is that the resource API will return a WritableResource
We can read the current value of the resource by using the valuestatuserror
The code above will print the following:
Value: undefined
Status: 'loading'
Error: undefined
Value: { id: 1, title: "Hello World", completed: false }
Status: 'resolved'
Error: undefined
Let's see how we can update the data locally.
import { resource } from "@angular/core";
@Component({
template: `
`
})
export class MyComponent {
todoResource = resource({
loader: () => {
return Promise.resolve({ id: 1, title: "Hello World", completed: false });
},
});
updateTodo() {
this.todoResource.value.update((value) => {
if (!value) return undefined;
return { ...value, title: "updated" };
});
}
}
We can update the data locally by using the updatevalue
This will print the following:
Value: { id: 1, title: "updated", completed: false }
Status: 'local'
Error: undefined
The 'local' status means that the data was updated locally.
Let's make a proper request to the server. Let's load some todos from the JSONPlaceholder API.
interface Todo {
id: number;
title: string;
completed: boolean;
}
@Component()
export class MyComponent {
todosResource = resource({
loader: () => {
return fetch(`https://jsonplaceholder.typicode.com/todos?_limit=10`)
.then((res) => res.json() as Promise
This todosResource
Of course, the todosResource
The code above will print the following:
Value: undefined
Status: 'loading'
Error: undefined
Value: [{ id: 1, title: "Hello World", completed: false }, { id: 2, title: "Hello World", completed: false }, ...]
Status: 'resolved'
Error: undefined
Let's say we want to refresh the data when the user clicks on a button.
import { resource } from "@angular/core";
@Component()
export class MyComponent {
todosResource = resource({
loader: () => {
return fetch(`https://jsonplaceholder.typicode.com/todos?_limit=10`)
.then((res) => res.json() as Promise
The refreshrefresh
Let's say we want to load the todos based on a todoId
import { resource } from "@angular/core";
@Component()
export class MyComponent {
todoId = signal(1);
todoResource = resource({
loader: () => {
return fetch(
`https://jsonplaceholder.typicode.com/todos/${this.todoId()}`
).then((res) => res.json() as Promise
This will work fine, but one this to notice is that loaderuntrackedtodoId
We want our resource to refresh the data (call the loader again) every time the todoIdrequest
todoResource = resource({
request: this.todoId,
loader: ({ request: todoId }) => {
return fetch(
`https://jsonplaceholder.typicode.com/todos/${todoId}`,
).then((res) => res.json() as Promise
Now, when the todoId
What if we have previous unfinished requests? Let's say we want to cancel the previous request when the todoIdabortSignal
todoResource = resource({
request: this.todoId,
loader: ({ request: todoId, abortSignal }) => {
return fetch(
`https://jsonplaceholder.typicode.com/todos/${todoId}`,
{ signal: abortSignal }
).then((res) => res.json() as Promise
This will cancel the previous request when the todoId
We can also have multiple request dependencies, for example:
limit = signal(10);
query = signal('');
todosResource = resource({
request: () => ({ limit: this.limit(), query: this.query() }),
loader: ({ request, abortSignal }) => {
const { limit, query } = request as { limit: number; query: string };
return fetch(
`https://jsonplaceholder.typicode.com/todos?_limit=${limit}&query=${query}`,
{ signal: abortSignal }
).then((res) => res.json() as Promise
Now, the todosResourcelimitquerylimitqueryloader
If that's the case, the resource API will automatically update the data locally, but cancel the ongoing request.
By separating reactive values from the loader function, we can move the logic of the loader
Before:
todoResource = resource({
request: this.todoId,
loader: ({ request: todoId, abortSignal }) => {
return fetch(
`https://jsonplaceholder.typicode.com/todos/${todoId}`,
{ signal: abortSignal }
).then((res) => res.json() as Promise
After:
import { ResourceLoaderParams } from "@angular/core";
function todoLoader({ request: todoId, abortSignal }: ResourceLoaderParams
The todoLoaderResourceLoaderParamsrequest
Angular has always been about using Observables when it comes to data loading. This means, that we can use Observables to derive the data loading instead of using signals & promises.
In order to make this possible, we can use the rxResource
import { rxResource } from "@angular/core/rxjs-interop";
@Component()
export class MyComponent {
limit = signal(10);
todosResource = rxResource({
request: this.limit,
loader: (limit) => {
return this.http.get
This will make the request based on the limit signal, and the loaderlimit
And same as we can change local state with the signals, we can also change the local state with the observables implementation in the rxResource
We have 2 new primitives [resource, rxResource] that will help us make our life easier when dealing with data loading in Angular. These primitives have been requested for so long now, and will land in v19 as developer previews.

Intermediate workshop
Learn RxJS from the basics to mastery. Understand everything: from core concepts and simple operators all the way to best practices, custom operators and marble testing.
Learn more
Angular is bridging the gap between the new Signal-Forms architecture and the Template/Reactive Forms. This isn't just a minor update—it’s a fundamental shift in how we author custom form controls.

Accessibility doesn’t have to be hard. Follow a comic-style, hands-on journey building an accessible day selector with Angular Aria, learning comboboxes, listboxes, and real screen reader behavior along the way.

If Signals were Angular’s “aha!” moment, Zoneless is the “oh wow—this feels snappy” moment. With Angular v21, new apps use zoneless change detection by default. No more zone.js magic under the hood—just explicit, predictable reactivity powered by Signals.

Deploy one Angular app for many clients with unique configs—without breaking SSR or DX. Here’s how to unlock dynamic configuration.

CPU profiles are more than flame charts—they’re structured JSON files. Learn how nodes, samples, and time deltas form the backbone of DevTools performance data.

Profiling is easiest when it's real. Learn how to capture and make sense of CPU profiles in Node.js across scripts, threads, and processes—then apply it to your own projects.