
In Angular, signals (as part of Reactive Programming) are used to manage state and handle events in an application, often with the help of libraries like RxJS. However, with the introduction of Angular Signals (a new experimental feature), Angular introduces a different approach to managing reactivity in a more declarative and fine-grained manner.
What are Angular Signals?

Signals are used in Angular for state creation and management. A signal is a thin layer that encapsulates a value. Using Signal in Angular, we can construct reactive variables. Customers can read signals at any time; they don’t delay value. From simple to complicated, it may hold any value.SetTimeout()
, Promises, and other asynchronous elements should not be added to Angular signals since they are strictly synchronous. Signals can be read-only or writable. By employing signals to monitor every state of our application, we make it possible for Angular to quickly determine which areas of the display require updating.
The code this tutorial is available in this Github repository
Creating Signal in Angular
Signals are functions that allow us to obtain the updated value or update the value. Here’s how to create a signal:

Here we have created a signal and assigned value 50 to it.
Reading Signal
We can read a signal by calling it, as signals are functions, so you can call them as:

On the browser’s console, you can see the value which is held by the variable.
Writable Signal
Angular knows when to update getCalculatedValue
whenever we change a value. Here, the value is a writable signal while getCalculatedValue
is readonly.
constructor() {
const writableSignalEx: WritableSignal<number> = signal(100);
const getCalculatedValue: Signal<number> = computed(() => writableSignalEx() * 10);
console.log('Writable Signals: ' + getCalculatedValue());
}
Code language: JavaScript (javascript)
Setting Value to Signal
We can set the value to a signal by the set method.

Updating Signal Value
We can update signal values by calling the update method of the signal:

Angular keeps track of where signals are read and when they are updated using this method. The framework updates the DOM with new state and performs other tasks using this information. Reactivity is the capacity to react to shifting signal values across time.
Using Equality Functions in Signal
If the old and new values are the same, Signal does not check for new values when the equality function is used. Because it doesn’t cause any updates, the performance is cleaner.

What is computed() Expression and When to Use
A computed signal is a signal that produces its value based on other signals.
Since they don’t write to signals, all computed()
signals only return the result based on inputs; they don’t have any side effects. They become pure functions as a result. Computed()
does not allow writing to signals, hence it makes them pure functions. It makes writing decorative code easier.
It’s important to note that computed signals are both lazy evaluated and memoized, meaning that they don’t update when you revisit components or refresh the page; instead, they provide a cached value of the same value. If the value is changed, it reads for the difference.

We should consider the following points while using computed()
:
- The signal that is computed is read-only. Because
computed()
is continually listening to fresh results, avoid making changes there. There will be a compiler error if you attempt to change a computed signal. - Avoid using asynchronous calls in
calculated()
since it automatically updates the changes when signal reads change.

Making Use of the Signal and Computing Within Your Components

Interaction with Templates
Signal data that is modified in your component class is used by templates.
Double curly brackets can be used in your templates to obtain the signal value. In the event that signal values change, templates automatically update the values.
<p>Addition: {{getAddition()}}</p>
Code language: HTML, XML (xml)
Angular continuously checks for updated values and displays them on templates. Angular can get updated values by calling the updateAddition()
function.
Angular allows dynamic values to be bound into DOM attributes or properties.
Such as:
<button [disabled]="getAddition() < 0" (click)="updateAddition()">Update Addition</button>
Code language: HTML, XML (xml)
OR
<ul [attr.role]="updpateAddtion() > 0">
Code language: HTML, XML (xml)
Using Signal with Control Flow @if and @for
If and for blocks can be used to display condition-based DOM elements.
@if (getDetails()) {
<p>Using get details information here</p>
} @else {
<p>No information available yet</p>
}
Code language: HTML, XML (xml)
What is effect() and When to Use
Any procedure that occurs whenever one or more signal values change is called an effect. You can use the effect function to generate an effect. Effect()
will be required more frequently with programming that is more imperative. We ought to attempt declarative code. Effects operate in a reactive environment, and any code you invoke within an effect will also operate in a reactive environment.
Every effect runs at least once. Any signal value reads are tracked when an effect is running. The impact repeats if any of these signal values change. While the change detection procedure is underway, effects always run asynchronously.
While using effect(), we should keep in mind that:
- It will be easier to read and identify incorrect behavior if our function is smaller.
- In
untracked()
, add the remaining code after reading the signals. - Logging data being displayed and when it changes, either for analytics or as a debugging tool.
- Adding custom DOM behavior that can’t be expressed with template syntax.
Using the `effect()` API, you can respond to signal changes. It requires a callback function that runs each time the value of the signal changes. For ex:
effect(() => {
const min = this.getMin();
const max = this.getMax();
untracked(() => {
if (max > min) {
console.log(`Maximum value is: ${max}`);
}
});
});
Code language: JavaScript (javascript)
You can also assign the effect to a field :
import { Component, computed, Input, Signal, signal, WritableSignal } from '@angular/core';
@Component({
selector: 'app-signal-basics',
templateUrl: './signal-basics.component.html',
styleUrls: ['./signal-basics.component.scss'],
standalone: true
})
export class SignalBasicsComponent {
currentAddition = signal(50);
firstInput = signal(5);
secondInput = signal(50);
getAddition = computed(() => this.firstInput() + this.secondInput());
constructor() {
const writableSignalEx: WritableSignal<number> = signal(100);
const getCalculatedValue: Signal<number> = computed(() => writableSignalEx() * 10);
console.log('Writable Signals: ' + getCalculatedValue());
}
ngOnInit() {
console.log('Reading Signals: ' + this.currentAddition());
this.currentAddition.set(100);
console.log('Signals after set : ' + this.currentAddition());
this.currentAddition.update(value => value + 900);
console.log('Signals after update : ' + this.currentAddition());
}
updateAddition() {
this.firstInput.set(Math.floor(Math.random() * 100) + 1);
this.secondInput.set(Math.floor(Math.random() * 100) + 1);
}
}
Code language: JavaScript (javascript)
Destroying effects:
Remember that we should take care to remove these effects when they are no longer needed. Effects are automatically deleted when components are destroyed. By default, based on the circumstances, Angular will automatically clean up the effect function. When you create an effect inside a component, for instance, Angular will remove it when the component is removed. This also applies to directives and other code.
Clean up effects manually:
The onCleanup
function allows you to register a callback that is triggered either after the effect is removed or before the effect’s subsequent run starts.

Signals and Observables
Like signal towers sending messages, observables have emitters that emit values.
Two methods are available if you need to read a signal in Observable’s pipe()
:
- Using a join operator, you can transform Signal into Observable so that your Observable can respond to changes in Signal.
- If you only require the current value and do not wish to respond to Signal or its variations, you can either
subscribe()
or read Signal directly in your operators. You don’t needuntracked()
in this case because observable is not a reactive context.
The Best Practices to Adhere to When Utilizing Signals in Angular
Example 1: Showing/Hiding a DOM with Signals
Signal is a useful tool for displaying or hiding different parts of the DOM based on API responses. Here’s an example of how to accomplish this:
@Component({
selector: 'get-details',
template: `
<div *ngIf="!isGetDetails()">
// No information available yet
</div>
<div *ngIf="isGetDetails()">
// Using get details information here
</div>
`,
})
export class GetDetailsComponent {
getDetails: WritableSignal<boolean> = signal(false);
ngOnInit() {
this.getDetails();
}
getDetails() {
this.myService.getDetailsAPI().subscribe(data) => {
this.getDetails.set(true);
});
}
isGetDetails() {
return this.getDetails();
}
}
Code language: JavaScript (javascript)
Example 2: Using Angular Signals to Count, Sort, and Filter Data
In this example, we have developed a writable signal called books. It is made up of various values, like the book’s title, price, and availability. In order to obtain the filtered books based on their availability, a computed()
signal was then built. Additionally, we have arranged the books by price.
After that, Effects was introduced to record the modifications. The log will be printed inside the effects whenever we change the signal. The signal has been changed to update the logs.
After then, the signals were changed by including a new object and by making changes to the old object to cause the event.
Filtering, sorting, and counting data according to different circumstances are examples of more complicated scenarios that can be handled by combining signals, computed signals, and effects.
import { Component, computed, effect, signal, WritableSignal } from "@angular/core";
@Component({
selector: 'app-signals-exercises',
templateUrl: './signals-exercises.component.html',
styleUrls: ['./signals-exercises.component.scss'],
standalone: true
})
export class SignalsExercisesComponent {
books: WritableSignal<any> = signal([
{ price: 1000, title: 'School Books', isAvailable: true },
{ price: 2000, title: 'Story Books', isAvailable: false },
{ price: 3000, title: 'Top Novels', isAvailable: false }
]);
// Computed signal based on the isAvailable flag to rank and filter prices
getAvailableBooksSortedByPrice = computed(() => {
const filteredResult = this.books().filter((book: { isAvailable: any; }) => book.isAvailable);
return filteredResult.sort((a: { price: number; }, b: { price: number; }) => a.price - b.price);
});
constructor() {
// Effect to record the filtered books and the number of books left
effect(() => {
console.log(`Available Books: ` + this.getAvailableBooksSortedByPrice().length);
});
this.updateBookElements();
}
updateBookElements() {
// Change the writable signals values.
const updatingElement = this.books();
updatingElement[2].isAvailable = true;
this.books.set(updatingElement);
}
}
Code language: JavaScript (javascript)
Example 3: The Interaction Between Components Happens Reactively, Leveraging Angular’s Signals System
Signal Declaration:counterSignal(0)
creates a reactive signal with an initial value of 0.
Signals are reactive, and any component subscribed to the signal will automatically update when the signal’s value changes.
ChildOneComponent:
The increaseSignalCount
method increments the signal’s value by 1 whenever the button is clicked.
ChildTwoComponent:
The counterSignal()
method is used to retrieve the current value of the signal and display it in the template. This component will automatically re-render when the signal’s value changes.
ParentComponent:
The signal is shared via input binding to both child components. When the signal is updated by ChildOneComponent
, ChildTwoComponent
will react to this change without the need for manual change detection or subscriptions.
This component will be the parent, containing the signal and passing it to children.
import { Component } from '@angular/core';
import { signal } from '@angular/core/signals'; // Import signals from Angular
@Component({
selector: 'app-root',
template: `
...
<hr>
<div class="child-section">
<app-child-one [signal]="counterSignal"></app-child-one>
<div class="child-divider"></div>
<app-child-two [signal]="counterSignal"></app-child-two>
</div>
`
})
export class ParentComponent {
// Creating a signal with an initial value
counterSignal = signal(0);
}
Code language: JavaScript (javascript)
In the ParentComponent
, we declare a signal that holds an integer value. The signal is shared with both child-one and child-two components via input bindings.
This component will modify the signal state by updating its value.
import { Component, Input } from '@angular/core';
import { signal } from '@angular/core/signals'; // Import signals
@Component({
selector: 'app-child-one',
template: `
<div class="child-1">
<h3>Child 1: Modify Signal</h3>
<button class="button1" (click)="increaseSignalCount()">Increase Signal</button>
</div>
`
})
export class ChildOneComponent {
@Input() counterSignal: ReturnType<typeof signal>; // Receive the signal from parent
increaseSignalCount() {
this.counterSignal.set(this.counterSignal() + 1); // Increase signal value by 1
}
}
Code language: JavaScript (javascript)
In ChildOneComponent
, we receive the signal from the parent as an input. The increaseSignalCount()
method modifies the signal’s state by increasing its value when the button is clicked.
This component will react to the changes in the signal and update itself accordingly.
import { Component, Input } from '@angular/core';
import { signal } from '@angular/core/signals';
@Component({
selector: 'app-child-two',
template: `
<div class="child-2">
<h3>Child 2: Current Signal Value</h3>
<p>Signal Value: {{ counterSignal() }}</p> <!-- Display signal value -->
</div>
`
})
export class ChildTwoComponent {
@Input() counterSignal: ReturnType<typeof signal>; // Receive the signal from parent
}
Code language: HTML, XML (xml)
In ChildTwoComponent
, we also receive the signal and directly use counterSignal()
to retrieve its current value. The component will automatically re-render whenever the signal value changes.

Summary
Signals have the capacity to improve the effectiveness of identifying changes and updating content, as well as to handle complications that were previously difficult, is its most intriguing aspect. The idea of signals is not new and exclusive to Angular. In various frameworks, often with other names, they have existed for years.
It is evident that the signal serves as a container for the value we wish to monitor. While the compute function is being called, Angular is monitoring and noting the usage of other signals that it is aware of.
Signals make it simple to monitor modifications to application data. Because of the simplicity of the Signals API and the signal notion, a codebase that employs signals everywhere would still be easy to understand.
Using signals in Angular applications can lead to more declarative, fine-grained reactivity, improving both performance and readability. By adhering to best practices such as maintaining a single source of truth, signal composition, and optimizing change detection, developers can harness the power of signals for better state management and UI interactions. Signals, especially when used in combination with Angular’s features like lazy loading and services, offer a modern approach to building efficient, scalable applications.

Author's Bio:

Bharat Kadam has 8 years of experience as a senior software engineer at Mobisoft Infotech with a focus on Angular Frontend development. He is well-versed in Angular, React, Java and contemporary frontend/backend technologies. He is constantly keen to keep ahead of industry trends, is committed to producing clear, maintainable code and resolving challenging issues in the constantly changing frontend/backend environment.