signal()
The signal() function creates a writable reactive value. It takes an initialValue and returns a WritableSignal that you call to read, and mutate with .set() or .update() to write — notifying every dependent when the value changes.
Definition
signal() is the Angular function that creates a writable signal — a reactive value a component remembers and can change. You call it with an initial value, and it returns a WritableSignal: a getter you invoke to read the current value, plus .set() and .update() methods to change it. Changing a signal notifies every computed value, effect, and template binding that read it, which is how the view stays in sync with the data.
It is the most fundamental of Angular’s reactive primitives and the one every derived value is built on. For the concept behind it and when to reach for reactive state, see Angular signals — the basics. For deriving a read-only value from one or more signals, its companion is computed().
Syntax
import { signal } from "@angular/core";
const count = signal(initialValue);
Parameters
| Parameter | Description |
|---|---|
initialValue | The value the signal starts at. It can be any type — a number, string, boolean, array, or object. Unlike a component-input default it is a genuine starting value, returned by the first read and kept until you change it with .set() or .update(). |
options (optional) | A second argument such as { equal } supplies a custom equality function. Angular compares the next value to the current one and skips notifying dependents when they are considered equal; by default it uses Object.is. |
The one required argument is initialValue. Pass any value as the starting state; optionally pass an equal function when the default identity comparison is not the notion of equality you want, for example to treat two arrays with the same contents as unchanged.
Return value
signal() returns a WritableSignal<T> — a function you call to read the value, extended with methods to write it:
- Read — call the signal with no arguments:
count()returns the current value. Reading inside a template, acomputed, or aneffectregisters a dependency so that reader re-runs when the value changes. .set(value)— replaces the stored value outright and notifies dependents. Use it when the next value does not depend on the current one..update(fn)— passes the current value tofnand stores what it returns. Use it when the next value is derived from the current one, such ascount.update((n) => n + 1)..asReadonly()— returns a read-onlySignal<T>view of the same value, so you can expose a signal for reading without exposing its setters.
Writes are compared against the current value with the signal’s equality function (Object.is by default), and an equal write is a no-op that notifies nobody. Treat the stored value as immutable: pass a new object or array rather than mutating the existing one in place, so the equality check detects the change.
Examples
A counter — read, then set the next value
The canonical example. signal(0) starts the count at zero, the template reads it with count(), and clicking calls .set() with the next number, which re-renders just that binding.
The .update() form
When the next value depends on the current one, .update() reads the latest value and returns the next. Here two buttons increment and reset the same signal through .update() and .set().
A signal holding any type
// initialValue can be any type
const name = signal(""); // string
const open = signal(false); // boolean
const user = signal({ id: 1 }); // object — replace, do not mutate
Browser & runtime support
signal() is part of the @angular/core package and has been stable since Angular 17, working in every version after it including Angular 18. It can be called to create a signal anywhere, but reading a signal only tracks a dependency inside a reactive context — a template, a computed, or an effect. Creating an effect must happen in an injection context, such as a constructor, unless you pass an explicit injector.
Frequently asked questions
What does signal() return?
WritableSignal — a getter function you call to read the value, extended with .set(), .update(), and .asReadonly() methods. You read with count() and write with count.set(next) or count.update((n) => n + 1).What is the initialValue argument?
options argument can supply a custom equal function to control when a write is treated as a change.How do I read the value of a signal?
count() returns the current value. Reading it inside a template, computed, or effect also registers a dependency, so that reader updates automatically when the signal changes.When should I use .set() versus .update()?
.set(value) when the next value does not depend on the current one, such as writing a fresh string. Use .update((current) => next) when the next value is derived from the current one, such as incrementing a counter, because it reads the latest value each time.