All topics
RxJSintermediate

Subjects Explained

Explain what a Subject is, how it's both an Observable and an Observer, and why that makes it a multicasting primitive.

A plain Observable is unicast by default — every subscriber gets its own independent execution of the producer logic, unaware of any other subscribers. A Subject breaks this isolation: it's simultaneously an Observable (you can subscribe to it) and an Observer (it has .next(), .error(), .complete() methods you can call manually), which means it acts as a multicasting hub — call .next(value) once, and every current subscriber receives that same value at the same time.

A plain Observable is like a magician performing a private trick individually for each audience member who steps into the booth; a Subject is like a live stage show where everyone currently seated in the audience sees the same trick performed at the same exact moment.

Key Concepts

1
This dual nature is exactly what makes a Subject useful as an event bus or bridge between imperative code and the reactive world: something outside the RxJS pipeline (a click handler, a WebSocket's onmessage callback, a service method called imperatively) can call .next() on a Subject to inject a value into a stream that other parts of the app are subscribed to, without needing to construct a full custom Observable with its own producer function.
Subjectonmessage.next()
2
A critical interview distinction is that a plain Subject has no memory — a late subscriber who subscribes after a value was already emitted simply never receives that past value, only values emitted after they subscribed. This is precisely the gap that BehaviorSubject and ReplaySubject (covered separately) each fill in different ways, which is why interviewers often ask you to explain plain Subject first as the baseline before comparing it to its variants.
SubjectBehaviorSubjectReplaySubject
3
A thoughtful answer also flags that exposing a raw Subject publicly from a service is generally considered poor practice, since any consumer could call .next() on it and inject arbitrary values — the idiomatic pattern is keeping the Subject private and exposing only its .asObservable() view publicly, so external code can subscribe but can't push values into the stream themselves.
Subject.next().asObservable()