A Subject is both an Observable and a way to manually push new values into that stream, useful for building simple event buses and shared state services. This lesson covers the three most common Subject variants.
What Is a Subject?
A Subject is a special kind of Observable that is also an "Observer": it has .next(), .error(), and .complete() methods you can call directly to push values into the stream, which every current subscriber then receives.
This makes Subjects a natural fit for services that need to broadcast events or shared state to multiple, independent parts of an application.
import { Subject } from 'rxjs';
@Injectable({ providedIn: 'root' })
export class NotificationService {
private notification$ = new Subject<string>();
readonly notifications = this.notification$.asObservable();
notify(message: string) {
this.notification$.next(message);
}
}
Exposing .asObservable() publicly (rather than the raw Subject) prevents outside code from calling .next() on it directly, only the service itself should push new values.
Subject Variants
new Subject<T>() // no memory of past values, only live subscribers get new emissions
new BehaviorSubject<T>(initial) // remembers the current value; new subscribers get it immediately
new ReplaySubject<T>(n) // remembers the last n values for new subscribers
A plain Subject has no memory, subscribers only receive values emitted after they subscribed.
BehaviorSubject requires an initial value and always has a "current value" accessible via .value.
ReplaySubject replays a configurable number of past emissions to any new subscriber.
All three share the same .next()/.error()/.complete() API for pushing values.
Subjects Cheatsheet
Choosing the right Subject variant for your use case.
Type
Remembers
Best For
Subject
Nothing
One-off events, like a "button clicked" bus
BehaviorSubject
The current (latest) value
Shared state with a required initial value
ReplaySubject
The last N values
State where new subscribers need recent history
.next(value)
n/a
Push a new value into the stream
.value
n/a
Synchronously read the current value (BehaviorSubject only)
.asObservable()
n/a
Expose a Subject as a read-only Observable
BehaviorSubject for Shared State
BehaviorSubject is especially common for service-based shared state, since it always has a current value, any component that subscribes (even after the value was last set) immediately receives the latest state rather than waiting for the next change.
this.itemsSubject.value synchronously reads the current cart contents without needing to subscribe.
A Plain Subject as an Event Bus
A plain Subject is ideal for one-off, fire-and-forget events that unrelated parts of the app might want to react to, without needing to remember or replay past events to late subscribers.
For simple, synchronous shared state, Angular signals are often a simpler alternative to a BehaviorSubject, no .asObservable()/.value distinction, and automatic reactivity in templates without an async pipe. Reach for Subjects when you specifically need RxJS's operator ecosystem (debouncing, combining streams, etc.) around that state.
Common Mistakes
Exposing a raw Subject/BehaviorSubject publicly instead of .asObservable(), letting any consumer push arbitrary values into shared state.
Using a plain Subject when new subscribers need the current/latest value immediately, BehaviorSubject is the correct choice there.
Forgetting to call .complete() on Subjects that represent a finite, bounded stream, leaving them open indefinitely.
Reaching for a Subject-based service when a simple signal would model the same shared state more simply.
Key Takeaways
A Subject is both an Observable and an Observer, you can subscribe to it and call .next() on it.
BehaviorSubject remembers and immediately replays its current value to new subscribers.
ReplaySubject replays a configurable number of past values to new subscribers.
Expose Subjects as .asObservable() publicly to keep write access (.next()) private to the owning service.
Pro Tip
Keep the writable Subject/BehaviorSubjectprivate on a service, and expose only the read-only .asObservable() stream publicly; this enforces that state changes only happen through the service's own methods, not from arbitrary consumers.
You now understand Subjects and their variants. Next, learn about RxJS Operators to transform and combine streams effectively.