Sitemap

Angular Signals and New Features and Improvements in Angular 20 — EN

6 min readJun 23, 2025

--

One of the most significant innovations in the Angular ecosystem in recent years is undoubtedly the concept of Angular Signals. Introduced in Angular 16, “signals” have been further enhanced with Angular 20, ushering in a new era in the field of reactive programming. In this article, I will do my best to explain what Angular Signals are, the new capabilities introduced in Angular 20, and how they are used in real-world projects.

What are Angular Signals?

Signals are a reactive primitive designed to track changing state in your application and automatically update the parts of the UI that depend on it when changes occur. They provide a more precise and efficient change detection mechanism.

Reactivity in Angular

In Angular, reactivity was managed through Zone.js and RxJS Observables. Zone.js tracked asynchronous operations (such as HTTP requests, timers, and DOM events) to automatically detect changes occurring within the Angular application and update the relevant parts accordingly.
RxJS is a powerful functional reactive programming library used to manage asynchronous data streams.
With the introduction of Signals, Angular began combining the strengths of RxJS with a simpler and more direct form of reactivity.

The Concept of Signals and Their Core Structures (signal, computed, effect)

Angular Signals are built upon three main primitives:

  1. signal(): Used to store a mutable value in the application. You can change the value using the set() method and update the current value based on its existing state using the update() method.
import { signal } from '@angular/core'; 
// Signal comes directly from the @angular/core package, meaning the signal function is part of Angular’s core module.

const count = signal(0);

count.set(5);
console.log(count()); // 5

count.update(value => value + 1);
console.log(count()); // 6

2. computed(): Creates a signal that is derived from one or more signals’ values, recalculating only when the dependent signals change. It is based on the principle of lazy evaluation which helps prevent unnecessary computations.

import { signal, computed } from '@angular/core';

const price = signal(10);
const quantity = signal(2);

const total = computed(() => price() * quantity());
console.log(total()); // 20

price.set(12);
console.log(total()); // 24 (total is recalculated only when price changes)

3. effect(): Used to manage side effects when the value of one or more signals changes. For example, directly manipulating the DOM, logging messages to the console, or making API calls. Effects are automatically managed and cleaned up according to the application’s lifecycle.

import { signal, effect } from '@angular/core';

const message = signal('Hello');

effect(() => {
console.log('Message changed:', message());
});

message.set('World'); // "Message changed: World" will be logged to the console

New Features and Improvements in Angular 20 Signals

Angular 20 introduced a number of important enhancements that strengthen signals and simplify their usage.

Stable features include:

  1. Stabilization of the effect API: The effect function is now fully stable and ready for production use.
  2. Stabilization of the toSignal API: The utility function toSignal, which converts RxJS Observables and JavaScript Promises into Signals easily, has been stabilized.
  3. Stabilization of the linkedSignal API: The new linkedSignal API is also officially stabilized, allowing more complex connections between signals.

New Experimental Feature:

  1. resource API: Developed in version 19 the resource API allows starting an asynchronous operation whenever a signal changes and exposes the result as a signal.
const userId = signal('123');
const userResource = resource({
params: () => ({id: userId()}),
loader: ({request, abortSignal}): Promise<User> => {
return fetch(`users/${request.id}`, {signal: abortSignal});
},
});

In this code, when the userId signal changes, it will fetch the user with the specified identifier.

2. Streaming Resource: Together with Signals, real-time data from streaming sources like WebSockets can be reactively tracked.

dataStream = resource({
stream: () => {
return new Promise<Signal<ResourceStreamItem<string[]>>>((resolve) => {
const resourceResult = signal<{ value: string[] }>({ value: [] });
// WebSocket initialization and message handling logic
resolve(resourceResult);
});
},
});

3. httpResource: This is an experimental API built on top of httpClient that enables making HTTP requests through a signal-based reactive API.

userId = signal(1);
userResource = httpResource<User>(() =>
`https://example.com/v1/users/${this.userId()}`
});

httpResource sends an HTTP GET request to the specified URL each time the userId changes. It also provides values like isLoading, headers, and others.

Upgrade of Zoneless to Developer Preview

To use Zoneless, you need to add provideZonelessChangeDetection() and remove the zone.js polyfill from your angular.json file. This reduces Angular’s dependency on Zone.js, improving performance and paving the way for fully Zone.js-free applications in the future.

Other Important Updates and Improvements

Angular 20 not only introduces Signals and Zoneless but also brings significant improvements in several other areas:

  1. Boosting Server-Side Performance: Core SSR features like Incremental Hydration and Route-level Render Mode Configuration have reached a stable state. Incremental Hydration speeds up applications by loading and hydrating only the necessary parts of a page. Route-level Render Mode Configuration allows configuring the rendering mode (SSR, SSG, CSR) for different routes in the application.
  2. Chrome DevTools: Thanks to collaboration with the Angular DevTools and Chrome DevTools teams, Angular-specific performance data — such as component rendering and change detection cycles — are now directly visible in Chrome’s Performance panel.
  3. Angular Core Improvements: New features have been added to the createComponent function that enable dynamically applying directives and defining bindings, including inputBinding, outputBinding, and twoWayBinding.
    PS: I will write a more detailed article on this topic.
  4. Style Guide Updates and File Naming Simplification:
    In Angular version 20, significant updates have been made to the style guide, and the file naming conventions have been further simplified. One of the most notable changes is that when generating components, directives, services, or pipes using the ng generate commands, the CLI no longer automatically appends suffixes like .component.ts, .directive.ts, or .service.ts.
    For example:
    4.1 Instead of user.component.ts, it now generates user.ts
    4.2 Instead of person.component.html, it generates person.html

If, like me, you prefer the previous naming convention and want the CLI to append these suffixes again, you can add the following configuration under the schematics section in your angular.json file:

{
"projects": {
"app": {
"schematics": {
"@schematics/angular:component": { "type": "component" },
"@schematics/angular:directive": { "type": "directive" },
"@schematics/angular:service": { "type": "service" },
"@schematics/angular:guard": { "typeSeparator": "." },
"@schematics/angular:interceptor": { "typeSeparator": "." },
"@schematics/angular:module": { "typeSeparator": "." },
"@schematics/angular:pipe": { "typeSeparator": "." },
"@schematics/angular:resolver": { "typeSeparator": "." }
}
}
}
}

5. New Syntax Support in Angular Templates:

5.1 ** Operator: The ** operator can now be used directly in templates.

<div>{{ 2 ** 3 }}</div>
<!-- 2 to the power of 3 = 8 -->
<!-- that is, 2 * 2 * 2 -->

Or inside a binding:

<div [style.width.px]="2 ** colWidth"></div>
<!-- width = 2 raised to the power of colWidth -->

5.2 The in Operator: It is now possible to use the in operator with @if to check whether a specific key (e.g., “name”) exists within an object. This makes templates cleaner and more readable.

@if ('name' in user) {
<div>
{{ user.name }}
</div>
}

5.3 Untagged Template Literals: Template literals written with backticks (`) can now be used directly in Angular expressions.

<div [class]="'layout col-' + colWidth"></div>
<!-- Concatenation of 'col-' with a variable -->

Or in a more modern form:

<div [class]={`layout col-${colWidth}`}></div>
<!-- Template literal written with backticks -->

Previously, using operators like these and template literals in Angular templates was not possible and caused errors. Now, full support is available.

Deprecation of NgIf, NgFor, and NgSwitch

Due to the widespread adoption of built-in control flow features introduced in Angular v17, the old structural directives — *ngIf, *ngFor, and *ngSwitch — are being phased out and will eventually lose support.

PS: These directives are still functional for now, but the Angular team has indicated they may be completely removed by Angular v22.

The new syntax is as follows:

Before:

<!-- Old syntax -->

<div *ngIf="user"> {{ user.name }} </div>

<li *ngFor="let item of items"> {{ item }} </li>

<div [ngSwitch]="status">
<span *ngSwitchCase="'loading'">Loading...</span>
<span *ngSwitchCase="'success'">Success!</span>
<span *ngSwitchDefault>Unknown</span>
</div>

New:

<!-- New syntax -->

@if (user) {
<div> {{ user.name }} </div>
}

@for (item of items) {
<li> {{ item }} </li>
}

@switch (status) {
@case ('loading') {
<span>Loading...</span>
}
@case ('success') {
<span>Success!</span>
}
@default {
<span>Unknown</span>
}
}

Conclusion

The Angular Signals concept and the innovations introduced in Angular 20 take reactivity for Angular users to a new level. With less code, higher performance, and cleaner architecture, Signals are an ideal tool to enhance reactivity in both existing and new projects. By leveraging the knowledge shared in this article, you can start applying Angular Signals and the latest features in your own projects.

--

--

No responses yet