Angular v20 is out! This release adds many features that push signals further. Since signals arrived in Angular 16, developer experience keeps improving.
This article walks through major Angular v20 additions with practical examples.
1. Resource API: simpler async
Signals excel at reactive, synchronous work—but what about async tasks with unpredictable completion? The new Resource API addresses that.
The Resource API is a primitive for declaring async dependencies as part of the signal graph. A resource exposes status, value, and other properties as signals, so they integrate cleanly with other signal-based logic and templates.
Basic usage
@Component({
selector: 'app-user-profile',
template: `
@if (userResource.isLoading()) {
<div>Loading...</div>
} @else if (userResource.error()) {
<div>An error occurred</div>
} @else if (userResource.hasValue()) {
<div>
<h2>{{ userResource.value().name }}</h2>
<p>{{ userResource.value().email }}</p>
</div>
}
`
})
export class UserProfileComponent {
userResource = resource(() => this.fetchUserData());
private fetchUserData() {
// Fetch data asynchronously
return this.http.get<User>('/api/user');
}
}
Resource state
A resource has these states:
isLoading: whether data is being fetchederror: error information when something failshasValue: whether a value was obtainedvalue: the fetched value
All of these are signals, so templates can use them reactively.
2. HTTP Resource: even easier data fetching
Built on the Resource API, an experimental HTTP Resource runs HTTP requests reactively for data fetching.
Basic usage
@Component({
selector: 'app-data-display',
template: `
@if (dataResource.isLoading()) {
<div>Loading data...</div>
} @else if (dataResource.error()) {
<div>Error: {{ dataResource.error().message }}</div>
} @else if (dataResource.hasValue()) {
<div>
{{ dataResource.value() | json }}
</div>
}
`
})
export class DataDisplayComponent {
dataResource = httpResource(() => '/api/data');
}
3. Streaming from resources
The resource primitive now supports streaming values—handy when you need to stream responses to the client.
Streaming example
@Component({
template: `{{ dataStream.value() }}`
})
export class App {
// Put the WebSocket initialization logic here...
// ...
// Initialize the streaming resource
dataStream = resource({
stream: () => {
return new Promise<Signal<ResourceStreamItem<string[]>>>((resolve) => {
const resourceResult = signal<{ value: string[] }>({
value: [],
});
this.socket.onmessage = event => {
resourceResult.update(current => ({
value: [...current.value, event.data]
});
};
resolve(resourceResult);
});
},
});
}
4. Signal API family stabilized
linkedSignal, effect, afterNextRender, and afterEveryRender graduated to stable and are production-ready.
Practical example
@Component({
selector: 'app-example',
template: `
<div>{{ count() }}</div>
<button (click)="increment()">Increment</button>
`
})
export class ExampleComponent {
count = signal(0);
constructor() {
// Watch for value changes
effect(() => {
console.log('Count changed:', this.count());
this.updateAnalytics(this.count());
});
// Post-render processing
afterNextRender(() => {
this.updateDOM();
});
}
increment() {
this.count.update(v => v + 1);
}
private updateAnalytics(value: number) {
// Update analytics
}
private updateDOM() {
// Update the DOM
}
}
5. Signal-based forms in progress
Angular continues expanding signal support, including a signal-based evolution of the forms system.
Expected capabilities
-
Signal-based reactivity
-
More concise form definitions
-
Better performance
-
Stronger type safety
6. Host binding type checking
Host bindings attach dynamic values to a component or directive's host element. Angular v20 adds type checking for host properties and for both @HostBinding and @HostListener.
Type-check example
@Component({
...,
host: {
'role': 'slider',
'[attr.aria-valuenow]': 'value',
'[class.active]': 'isActive()',
'[tabIndex]': 'disabled ? -1 : 0',
'(keydown)': 'updateValue($event)',
},
})
export class CustomSlider {
value: number = 0;
disabled: boolean = false;
isActive = signal(false);
updateValue(event: KeyboardEvent) { /* ... */ }
/* ... */
}
7. Improved dynamic component creation API
The dynamic component API is a powerful way to create components programmatically in Angular apps.
Updated usage
@Component({
standalone: true,
template: `Hello {{ name }}!`
})
class HelloComponent {
name = 'Angular';
}
@Component({
standalone: true,
template: `<div id="hello-component-host"></div>`
})
class RootComponent {}
// Bootstrap the application
const applicationRef = await bootstrapApplication(RootComponent);
// Identify the DOM node to use as the host
const hostElement = document.getElementById('hello-component-host');
// Get the EnvironmentInjector instance from ApplicationRef
const environmentInjector = applicationRef.injector;
// Create a ComponentRef instance
const componentRef = createComponent(HelloComponent, {hostElement, environmentInjector});
// As the final step, register the newly created ref with the ApplicationRef instance
// and include the component view in the change-detection cycle
applicationRef.attachView(componentRef.hostView);
componentRef.changeDetectorRef.detectChanges();
8. Other quality-of-life improvements
TypeScript 5.8 support
TypeScript 5.8 is supported for better inference and developer experience.
Untagged template literal expressions in templates
Templates now support untagged template literal expressions.
<div>Balance for ${user.name}: ${balance | currency}</div>
Template HMR enabled by default
Template HMR (Hot Module Replacement) is on by default, shortening reload time during development.
Cleanup of unused imports
A new schematic removes unused imports from the application.
ng generate @angular/core:cleanup-unused-imports
Summary
Angular v20 is a strong release for signal-based development. Resource and HTTP Resource APIs, signal-based forms in progress, and more make Angular development increasingly enjoyable.
Standout themes:
-
Simpler async
- Unified async handling with the Resource API
- Easier data fetching with HTTP Resource
- Streaming for real-time data
-
Better developer experience
- Host binding type checks
- Improved dynamic component creation
- Template HMR on by default
-
Performance optimization
- Signal-based reactivity
- More efficient change detection
- Smaller bundle sizes
I want to use these features to build apps users truly love. I am excited to see what comes next!