Question

Subject vs BehaviorSubject in RxJS: What’s the Difference?

typescriptangularrxjsbehaviorsubjectsubject

Question

In RxJS, what is the difference between Subject and BehaviorSubject?

I am not sure whether the only difference is that BehaviorSubject provides a getValue() method, or whether there are important behavioral differences in how they emit values to subscribers.

Short Answer

By the end of this page, you will understand the core difference between Subject and BehaviorSubject in RxJS: a BehaviorSubject stores the latest value and immediately gives it to new subscribers, while a Subject only emits values that happen after subscription. You will also see when to use each one, common mistakes, and how this choice appears in real codebases.

Concept

Subject and BehaviorSubject are both special RxJS types that are observables and observers at the same time.

That means they can:

  • emit values using next()
  • be subscribed to like any observable

The important difference is how they treat the current value.

Subject

A Subject does not keep a stored current value for new subscribers.

  • If a value was emitted before you subscribed, you will not receive it.
  • You only receive values emitted after your subscription starts.

BehaviorSubject

A BehaviorSubject always has a current value.

  • It must be created with an initial value.
  • When a new subscriber subscribes, it immediately receives the latest stored value.
  • After that, it continues receiving future emissions.

Why this matters

This difference is important whenever your app needs to answer the question:

"What is the current state right now?"

Examples:

  • current logged-in user
  • current theme (dark or light)
  • current loading state
  • current search text
  • current cart total

In those cases, BehaviorSubject is often useful because new subscribers can instantly get the latest known state.

If you only care about future events and do not need a current stored value, Subject is often enough.

About getValue()

getValue() is not the main difference. It is a convenience method that exists because BehaviorSubject stores the latest value.

The real difference is this:

  • Subject: no stored current value for new subscribers
  • BehaviorSubject: stores the latest value and replays it immediately to new subscribers

Mental Model

Think of a Subject like a live radio broadcast.

  • If you tune in late, you only hear what is broadcast from that moment onward.
  • You miss everything that was already said.

A BehaviorSubject is like a radio station that first tells you the current status before continuing live.

  • When you join, it immediately tells you the latest update.
  • Then it keeps giving you new live updates.

Another analogy:

  • Subject = a doorbell: you only hear rings that happen while you are there.
  • BehaviorSubject = a thermostat display: you can always see the current temperature immediately, then notice future changes.
Take Quiz

Syntax and Examples

Basic syntax

import { Subject, BehaviorSubject } from 'rxjs';

const subject = new Subject();
const behaviorSubject = new BehaviorSubject('initial value');

Example: Subject

import { Subject } from 'rxjs';

const subject = new Subject();

subject.next('A');

subject.subscribe(value => {
  console.log('Subscriber 1:', value);
});

subject.next('B');
subject.next('C');

Output:

Subscriber 1: B
Subscriber 1: C

Why?

The value 'A' was emitted before the subscription, so the subscriber never receives it.

Example: BehaviorSubject

import { BehaviorSubject } from 'rxjs';

const behaviorSubject = new BehaviorSubject('A');

behaviorSubject.subscribe(value => {
  console.log('Subscriber 1:', value);
});

behaviorSubject.next('B');
behaviorSubject.next('C');

Output:

Subscriber 1: A
Subscriber 1: B
Subscriber 1: C

Why?

The subscriber immediately receives the current stored value 'A', then gets future values.

Late subscription example

import { Subject, BehaviorSubject } from 'rxjs';

const subject = new Subject();
const behavior = new BehaviorSubject(0);

subject.next(1);
behavior.next(1);

subject.subscribe(value => console.log('Subject:', value));
behavior.subscribe(value => console.log('BehaviorSubject:', value));

subject.next(2);
behavior.next(2);

Output:

BehaviorSubject: 1
Subject: 2
BehaviorSubject: 2

Explanation

  • The Subject subscriber misses 1 because it subscribed too late.
  • The BehaviorSubject subscriber immediately receives 1, which is the current value at subscription time.
  • Then both receive 2.

Step by Step Execution

Consider this example:

import { BehaviorSubject } from 'rxjs';

const status$ = new BehaviorSubject('offline');

status$.next('connecting');

status$.subscribe(value => {
  console.log('Subscriber A:', value);
});

status$.next('online');

Here is what happens step by step:

  1. const status$ = new BehaviorSubject('offline');

    • A BehaviorSubject is created.
    • Its current value is 'offline'.
  2. status$.next('connecting');

    • The current value changes from 'offline' to 'connecting'.
    • There are no subscribers yet, so nothing is logged.
  3. status$.subscribe(...)

    • A new subscriber joins.
    • Because this is a BehaviorSubject, it immediately receives the latest current value.
    • The latest value is 'connecting'.
    • Console output: Subscriber A: connecting
  4. status$.next('online');

    • The current value becomes 'online'.
    • Existing subscribers receive it.
    • Console output: Subscriber A: online

Final output:

Subscriber A: connecting
Subscriber A: online

Now compare with Subject:

import { Subject } from 'rxjs';

const status$ = new Subject();

status$.next('connecting');

status$.subscribe(value => {
  console.log('Subscriber A:', value);
});

status$.next('online');

Output:

Subscriber A: online

The earlier 'connecting' value is lost for that subscriber because Subject does not store the latest value.

Real World Use Cases

When to use Subject

Use Subject for events that do not need a remembered current value.

Examples:

  • button click event streams
  • refresh notifications
  • modal open/close triggers
  • "user submitted the form" events
  • internal app events between services
const refresh$ = new Subject();

refresh$.subscribe(() => {
  console.log('Reload data');
});

refresh$.next();

Here, you usually care about the event happening now, not the last event from earlier.

When to use BehaviorSubject

Use BehaviorSubject for state that should always have a current value.

Examples:

  • current user
  • current selected language
  • loading status
  • current page number
  • current filter settings
const loading$ = new BehaviorSubject(false);

loading$.subscribe(value => {
  console.log('Loading:', value);
});

loading$.next(true);
loading$.next(false);

A new subscriber can immediately know whether loading is currently happening.

Rule of thumb

  • Use Subject for events
  • Use BehaviorSubject for state
Take Quiz

Real Codebase Usage

In real projects, developers often choose between these two based on whether they are modeling an event stream or a state container.

Common BehaviorSubject pattern: service state

import { BehaviorSubject } from 'rxjs';

class AuthService {
  constructor() {
    this.userSubject = new BehaviorSubject(null);
    this.user$ = this.userSubject.asObservable();
  }

  login(user) {
    this.userSubject.next(user);
  }

  logout() {
    this.userSubject.next(null);
  }
}

Why this is common:

  • null acts as the initial state
  • components can subscribe at any time
  • new subscribers immediately get the latest user state
  • exposing user$ instead of the subject prevents outside code from calling next() directly

Common Subject pattern: action trigger

import { Subject } from 'rxjs';

class SearchService {
  constructor() {
    this.searchRequestedSubject = new Subject();
    this.searchRequested$ = this.searchRequestedSubject.asObservable();
  }

  requestSearch(term) {
    this.searchRequestedSubject.next(term);
  }
}

Why this is common:

  • each search request is an event
  • there is no meaningful "current search request" to keep forever
  • subscribers only need future requests

Common patterns around them

Encapsulation with asObservable()

A common practice is:

  • keep the subject private
  • expose only the observable
this.stateSubject = new BehaviorSubject({ count: 0 });
this.state$ = this.stateSubject.asObservable();

This prevents accidental writes from other parts of the code.

Guard clauses and validation before next()

updateUser(user) {
  if (!user || !user.id) {
    return;
  }

  this.userSubject.next(user);
}

Loading and error state

Developers often use BehaviorSubject for values like:

  • isLoading$
  • error$
  • selectedItem$
  • theme$

because every new subscriber should instantly know the current state.

Common Mistakes

1. Thinking BehaviorSubject is only Subject plus getValue()

This is the main misunderstanding.

The biggest difference is immediate replay of the latest value to new subscribers.

2. Forgetting that BehaviorSubject needs an initial value

This is required:

const count$ = new BehaviorSubject(0);

This is invalid:

const count$ = new BehaviorSubject();

Always choose an initial value that makes sense, such as:

  • 0
  • false
  • null
  • []
  • {}

3. Using Subject when you really need current state

Broken idea:

const theme$ = new Subject();
theme$.next('dark');

theme$.subscribe(value => {
  console.log('Theme:', value);
});

The subscriber gets nothing until another value is emitted.

Better:

const theme$ = new BehaviorSubject('light');
theme$.next('dark');

theme$.subscribe(value => {
  console.log('Theme:', value);
});

Now the subscriber immediately gets 'dark'.

4. Overusing getValue()

getValue() can be useful, but relying on it too much can lead to imperative code instead of reactive code.

Prefer subscriptions or observable pipelines when possible.

const count$ = new BehaviorSubject(5);
console.log(count$.getValue());

This works, but in many cases it is cleaner to react through the observable stream.

5. Exposing the subject directly

Risky:

this.userSubject = new BehaviorSubject(null);

If other files can access userSubject, they can call next() from anywhere.

Safer:

this._userSubject = new BehaviorSubject(null);
this.user$ = this._userSubject.asObservable();

6. Confusing BehaviorSubject with replaying many values

BehaviorSubject replays one latest value, not a whole history.

If you need multiple past values for new subscribers, you may be thinking of ReplaySubject, not BehaviorSubject.

Comparisons

FeatureSubjectBehaviorSubject
Requires initial valueNoYes
Stores current valueNoYes
New subscriber gets latest value immediatelyNoYes
Good for event streamsYesSometimes
Good for stateUsually noYes
Has getValue()NoYes

Subject vs BehaviorSubject in one sentence

  • Subject is for future events.
  • BehaviorSubject is for current state plus future updates.

Related comparison: BehaviorSubject vs ReplaySubject

FeatureBehaviorSubjectReplaySubject
Needs initial valueYesNo
Replays latest valueYesYes
Replays multiple old valuesNoYes, if configured
Best for current stateYesSometimes

If you only need the current latest value, BehaviorSubject is often simpler.

Take Quiz

Cheat Sheet

Quick reference

Subject

const subject = new Subject();
subject.next(value);
subject.subscribe(value => console.log(value));
  • no initial value required
  • does not store the latest value
  • new subscribers only get future emissions
  • best for events

BehaviorSubject

const state$ = new BehaviorSubject(initialValue);
state$.next(newValue);
state$.subscribe(value => console.log(value));
const current = state$.getValue();
  • requires an initial value
  • stores the latest value
  • new subscribers immediately receive the current value
  • best for state

Rules of thumb

  • event stream -> Subject
  • current app state -> BehaviorSubject
  • need immediate latest value on subscribe -> BehaviorSubject
  • no meaningful initial value and no need for current state -> Subject

Common initial values

new BehaviorSubject(null)
new BehaviorSubject(false)
new BehaviorSubject([])
new BehaviorSubject({})
new BehaviorSubject(0)

Important edge case

A BehaviorSubject always emits its current value to a new subscriber immediately, even if that value was set before the subscriber existed.

FAQ

Is BehaviorSubject just Subject with getValue()?

No. The main difference is that BehaviorSubject stores the latest value and immediately sends it to new subscribers.

Why does BehaviorSubject need an initial value?

Because it always represents a current value. RxJS needs to know what that current value is from the start.

When should I use Subject instead of BehaviorSubject?

Use Subject for one-time or ongoing events where you do not need to remember the current state, such as clicks, refresh triggers, or notifications.

When should I use BehaviorSubject?

Use it when your app needs a current state that new subscribers should receive immediately, such as the current user, loading status, or selected item.

Do late subscribers miss values with Subject?

Yes. A Subject does not replay old values. Late subscribers only receive future emissions.

Do late subscribers miss values with BehaviorSubject?

They may miss older history, but they will always receive the latest current value immediately.

Can BehaviorSubject replace all Subject usage?

Not always. If you are modeling pure events rather than state, Subject is often a better fit and avoids inventing unnecessary initial values.

Take Quiz

Mini Project

Description

Build a small reactive status store for an application. This project demonstrates why BehaviorSubject is useful for storing current state and why Subject is useful for fire-and-forget events. You will model a user's online status as state and a refresh action as an event.

Goal

Create a simple RxJS example where new subscribers instantly receive the current status from a BehaviorSubject, while refresh events are handled through a Subject.

Requirements

  • Create a BehaviorSubject to store the user's current status
  • Create a Subject to represent refresh button clicks
  • Add at least one subscriber before updating values and one subscriber after updating values
  • Show in the output that late subscribers receive the latest status but do not receive old refresh events
Take Quiz

Keep learning

Related questions

View more