Skip to content

Angular didn't become reactive until Signals

Why this claim is historically wrong — and why it can get dangerous in real projects.

A common claim goes:

“Angular didn’t have reactive programming until Signals.”

At first glance, the claim sounds plausible.

Since Angular 16, APIs like signal(), computed(), and effect() sit visibly in Angular core. That’s made reactivity more present, both in vocabulary and in concept. Anyone who only started working seriously with Angular in the Signals era can easily get the impression that Angular was mainly component- and class-oriented before Signals, and became reactive afterward.

The problem: this reading confuses visibility with origin.

Signals didn’t make Angular reactive.

Signals gave Angular a new, framework-native reactivity primitive.

That’s an important difference.

Myth vs. reality

The claim isn’t just a wrong historical framing.

It’s dangerous because it can function as a convenient excuse on real projects.

If reactivity supposedly only showed up with Angular 16, then nobody needed to learn it back in Angular 2, 4, 8, 12, or 15. Then imperative components, manual subscriptions, local mutations, and unclear data flow weren’t the result of bad architecture decisions — they were supposedly just where the framework stood at the time.

That’s convenient.

But it’s wrong.

Claims like this often work as a defense against uncomfortable questions:

  • Why did nobody ever work cleanly with Observable streams?
  • Why aren’t there any clear ViewModels?
  • Why is business logic sitting in components?
  • Why does state get synchronized manually?
  • Why are there subscribe() blocks everywhere?
  • Why is there no clear separation between read state and commands?
  • Why is the template full of logic?
  • Why is refactoring barely possible after a few years?

The answer stops being:

“We didn’t understand the paradigm, didn’t learn it, or didn’t prioritize it.”

It becomes:

“That didn’t even exist back then.”

That’s exactly where the danger lies.

Because if the diagnosis is wrong, the decisions built on it are wrong too.

Cause-and-effect diagram

The damage isn’t in the sentence — it’s in the codebase

Section titled “The damage isn’t in the sentence — it’s in the codebase”

One wrong sentence doesn’t build bad architecture by itself.

But when sentences like this justify decisions for years, you end up with codebases that eventually become almost impossible to change.

Then an Angular project after seven years might look like this:

  • components with thousands of lines of code
  • imperative loading and error states everywhere
  • nested subscriptions
  • manual synchronization between fields
  • services acting as global data dumps
  • templates full of function calls and hidden logic
  • no clean domain boundaries
  • no consistent state model
  • presentation logic that’s barely testable
  • high effort for every change
  • fear of framework upgrades
  • dependency on old patterns nobody ever deliberately chose

And then the bitter contradiction shows up:

The team is locked into the framework, yet it never actually used the framework’s core concepts properly.

That’s real framework lock-in.

Not because Angular locks you in by design.

But because the codebase was built so heavily against the framework’s paradigm that every modernization effort gets expensive.

The correct historical framing is:

Angular wasn’t reactive only starting with Signals. Angular has been heavily shaped by Observables and RxJS since Angular 2. Since Angular 16, Signals add a framework-native, fine-grained reactivity primitive on top of that model.

That’s the truth in one sentence.

And it matters, because it puts responsibility where it belongs.

Angular 2 through 15 weren’t “pre-reactive.”

They were Observable-based.

Anyone who built Angular completely imperatively during that time wasn’t just writing “the Angular style of the day.” They were ignoring core concepts of the framework, or only using them superficially.

That’s not a moral accusation.

But it is an important technical diagnosis.

The historical punchline: Angular 2 was already the reactive break

Section titled “The historical punchline: Angular 2 was already the reactive break”

To really understand the fallacy, you have to keep Angular and AngularJS apart.

AngularJS was heavily shaped by $q, promises, $http, $scope, $watch, and the digest cycle. The AngularJS documentation describes $q as a Promises/A+-compatible implementation of promises and deferred objects.

Source: AngularJS Docs: $q

That was the AngularJS world: asynchronous flows were often modeled with promises and carried into the UI through the digest cycle.

Angular 2 wasn’t a minor update here.

Angular 2 was a break.

Even the old Angular 2 documentation, describing the HTTP client, states that http.get() doesn’t return a promise but an Observable<Response> from RxJS. Right after that, the documentation describes RxJS as a library Angular supports, and says Observables are used widely across Angular applications.

Source: Angular v2 Archive: HTTP Client / RxJS library

The Angular 2 glossary also describes Observables as part of Angular itself, connecting them to Angular’s event system and HTTP client, among other things.

Source: Angular v2 Archive: Glossary / Observable

Angular 2 already supported reactive template binding as well. The old Angular 2 documentation for AsyncPipe describes it as accepting a promise or an Observable, subscribing automatically, and forwarding the emitted values.

Source: Angular v2 Archive: Pipes / AsyncPipe

That’s the decisive historical point:

Angular wasn’t reactive only starting with Signals. Angular 2 was already the departure from the AngularJS promise world toward an Observable- and RxJS-shaped architecture.

Signals came much later.

They didn’t introduce the reactive idea to Angular. They added another reactive model on top: closer to the framework, more synchronous, more fine-grained, and more focused on local and derived state.

Angular 2 was already the reactive break.

So the timeline isn’t:

AngularJS: imperative Angular 2 through 15: not reactive Angular 16: finally reactive

It’s:

AngularJS: heavily shaped by promises, $q, and the digest cycle Angular 2: a fresh start based on Observables and RxJS Angular 16: Signals as an additional framework-native reactivity primitive

That’s where the myth technically falls apart.

Because anyone who says:

“Angular didn’t have reactive programming until Signals.”

is skipping right past Angular 2’s central architecture decision.

Angular 2 didn’t just tolerate Observables somewhere by accident. Angular 2 made them visible in the HTTP client, in the documentation, in the event model, and in template binding.

That wasn’t a footnote.

That was a paradigm shift compared to AngularJS.

PhaseDominant modelMeaning
AngularJS$q, promises, $http, $scope, $watch, digest cycleAsynchronous work was usually modeled with promises and pushed into the UI through the digest cycle.
Angular 2RxJS, Observables, Http, event system, AsyncPipeAngular visibly shifted to an Observable- and RxJS-shaped architecture.
Angular 16Signals, computed, effect, RxJS interopAngular added a framework-native, fine-grained reactivity primitive.

This timeline matters.

Signals weren’t Angular’s break with AngularJS.

Angular 2 was.

Signals are the next step within Angular’s own evolution.

Today’s Angular is still Observable-based

Section titled “Today’s Angular is still Observable-based”

The Observable model isn’t a historical accident that later disappeared.

Current Angular documentation still describes Observables as the interface Angular uses to handle typical asynchronous operations: HTTP uses Observables for requests and responses, and the router and forms use Observables to react to user input events.

Source: Angular Docs: Observables in Angular

Current documentation for HttpClient also still says that request methods return RxJS Observables.

Source: Angular Docs: Making HTTP requests

The current HttpClient API documentation shows return types like Observable<T>, Observable<HttpEvent<T>>, and Observable<HttpResponse<T>>.

Source: Angular API: HttpClient

So RxJS isn’t just baggage left over from the past.

It’s still part of everyday Angular work.

That said, Signals shouldn’t be talked down either.

Angular 16 introduced Signals: a new signals library inside @angular/core, plus an interop layer to RxJS via @angular/core/rxjs-interop.

Source: Angular Blog: Angular v16 is here

Current Signals documentation describes Signals as a system that tracks, at a granular level, how and where state is used in an application, so Angular can optimize rendering updates.

Source: Angular Docs: Signals

Typical Signal APIs look like this:

const count = signal(0);
const doubled = computed(() => count() * 2);
effect(() => {
console.log('Count changed:', count());
});

That’s a different form of reactivity than an RxJS stream.

Signals are especially strong for synchronous, local, derivable state:

  • UI state
  • local component state
  • derived values
  • fine-grained updates
  • simple dependencies between values
  • better readability for certain state relationships

RxJS, on the other hand, is especially strong for asynchronous data streams:

  • HTTP
  • router state
  • WebSockets
  • events
  • forms
  • debouncing
  • cancellation
  • combining multiple sources
  • complex timing behavior

Both are reactive.

But they solve different problems.

Both are reactive. Not every problem needs the same tool.

Angular itself doesn’t treat RxJS and Signals as either-or

Section titled “Angular itself doesn’t treat RxJS and Signals as either-or”

Angular ships its own interop layer to connect RxJS and Signals.

Current documentation for @angular/core/rxjs-interop describes toSignal(): it lets you create a signal that tracks an Observable’s value. The docs compare this behavior to the async pipe, just more flexible and usable outside templates too.

Source: Angular Docs: RxJS interop with Angular signals

That disproves the myth too.

If Signals were the first real reactivity in Angular, Angular wouldn’t need an official bridge between Observables and Signals.

That bridge exists precisely because Angular has historically been heavily Observable-based, and Signals don’t replace that model — they add to it.

The fallacy usually happens because a lot of people use Angular without ever consistently learning the reactive paradigm behind it.

You can write Angular like this for a very long time:

this.http.get<User[]>('/api/users').subscribe((users) => {
this.users = users;
});

That works.

But it’s not a reactive architecture model.

It’s often just imperative code with an Observable bolted onto the front.

One subscribe call doesn't make a reactive architecture.

Reactive Angular code doesn’t just ask:

How do I get the value?

It asks:

  • Where does this state come from?
  • How does it change over time?
  • What’s derived state?
  • What’s a command?
  • What’s a read model?
  • What belongs in the ViewModel?
  • What should the template only ever consume?
  • Which dependencies should be modeled explicitly?
  • Where does presentation logic end?
  • Where does business logic begin?

RxJS was there for this long before Signals existed.

Observable, map, switchMap, combineLatest, shareReplay, AsyncPipe, facades, stores, and ViewModels weren’t suddenly possible starting with Angular 16.

A lot of teams just never used these tools consistently.

Technical half-truths are rarely just knowledge gaps.

On real projects, they often turn into shields.

They protect old decisions from scrutiny.

They protect codebases that grew over time from uncomfortable questions.

They protect assumptions about roles, in organizations where frontend was understood as just a UI layer.

They protect teams from having to seriously keep up with documentation, paradigms, and framework evolution.

The problem isn’t that someone doesn’t know something.

The problem starts when not knowing turns into an architectural justification.

“That wasn’t possible back then.”

is a strong claim.

If it’s wrong, it hides the real cause.

Maybe it was possible after all.

Maybe it was documented.

Maybe it was even the recommended approach.

Maybe it just never got learned, understood, or prioritized.

That’s uncomfortable.

But for an honest technical analysis, that exact distinction is what matters.

Bad architecture doesn’t stay contained in the code.

It hits the product.

When an Angular frontend gets built imperatively, unstructured, and against reactive data flow for years, every business change gets more expensive:

  • new features take longer
  • bugs get harder to reproduce
  • side effects become hard to follow
  • refactoring gets risky
  • tests stay shallow
  • UI states become inconsistent
  • releases get more cautious
  • technical debt starts dictating product planning

At some point, the product no longer decides what gets built.

The codebase decides what’s still affordable.

That’s the point where architecture stops being an internal developer concern.

It becomes a product problem.

The interesting question isn’t:

RxJS or Signals?

The better question is:

Which reactivity model fits which problem?

An HTTP request that gets triggered by a search term and needs to cancel stale requests is often a very good RxJS problem.

Local UI state, a derived value, or a simple state relationship inside a component is often a very good Signal problem.

A store can touch both worlds.

A facade can consume Observables and expose Signals.

A signal can come from an Observable.

An Observable can come from a signal.

Angular itself ships interop for exactly this. That’s a strong sign that Angular doesn’t treat RxJS and Signals as either-or.

A good Angular architecture doesn’t come from writing signal() everywhere.

It comes from clean ownership.

More important than the specific API are questions like:

  • Is business logic separated from the template?
  • Is there a clear ViewModel?
  • Are commands and read state separated?
  • Is the data flow traceable?
  • Are side effects controlled?
  • Is derived state actually derived, or is it manually synchronized?
  • Are components consumers, or are they dumping grounds?
  • Are services cut cleanly, or are they just manager classes?
  • Is the code testable?
  • Is the architecture understandable?

Whether the concrete state ends up modeled with RxJS, Signals, the NgRx Signal Store, or some combination is an implementation question.

The paradigm behind it matters more than the API.

The API is the tool. Architecture is the decision about who's responsible for what.

Instead of:

“Angular didn’t have reactive programming until Signals.”

better:

“Angular has been heavily shaped by Observables and RxJS since Angular 2. Since Angular 16, Signals add a framework-native, fine-grained reactivity primitive on top of that model.”

This phrasing is historically more accurate.

It gives Signals credit without erasing the years before it.

And it stops old architecture decisions from getting retroactively legitimized by a false framework history.

Instead of saying:

“Angular just wasn’t reactive back then.”

you should ask:

“Which reactive concepts were available to us back then — and why didn’t we use them?”

That’s the more uncomfortable question.

But it’s the more useful one.

Because it doesn’t lead to excuses. It leads to learning:

  • Which paradigms did we overlook?
  • Which patterns did we apply wrong?
  • Which decisions were deliberate at the time?
  • Which were just habit?
  • Which parts of the codebase do we need to stabilize?
  • Which concepts do we need today to make the codebase easier to change again?

That’s when real architecture work begins.

Not through blame.

Through precise diagnosis.

The claim

“Angular didn’t have reactive programming until Signals.”

is wrong, both historically and technically.

Angular has been Observable- and RxJS-shaped since Angular 2. The official Angular 2 documentation describes Observables in the HTTP client, the event system, and the AsyncPipe. Current Angular documentation still uses Observables in HttpClient. Angular 16 introduced Signals as a new, framework-native reactivity primitive — complete with official RxJS interop.

So Signals aren’t Angular reactivity’s birth certificate.

Signals are a new chapter.

An important chapter.

A good chapter.

Just not chapter one.

And that’s exactly why the myth is dangerous.

It turns missed learning into a framework excuse.

It turns architectural neglect into historical necessity.

It blocks the question that should get asked in old Angular codebases:

What was actually limited by Angular — and what did we just never properly learn?

That question is uncomfortable.

But without it, you don’t just stay trapped in the framework.

You stay trapped in your own past.

Sources checked on June 10, 2026.