Hot vs Cold Observables: Getting Started with RxJS - IV
I recently came across MkDocs-Material by Martin Donath, a fantastic open-source project with over 22k GitHub stars. It’s an incredible contribution to the community, making documentation hosting effortless. While exploring it, I got curious about how such a large project achieves reactiveness. The stack is mostly HTML, SCSS, Preact, RxJS, and a few workers, and I saw this as the perfect opportunity to dive into RxJS—especially how it utilizes Observables and other advanced patterns. I wrote an article on multicast yesterday, check it out: Multicast & Chill: Getting Started with RxJS - III Athreya aka Maneshwar ・ Feb 13 #webdev #programming #javascript #beginners Operators & Chill: Getting Started with RxJS - II Athreya aka Maneshwar ・ Feb 12 #webdev #programming #javascript #beginners Observables & Chill: Getting Started with RxJS - I Athreya aka Maneshwar ・ Feb 11 #webdev #programming #javascript #beginners RxJS observables come in two flavors: hot and cold. Understanding the difference between them is crucial when working with reactive programming, as it affects how data is produced and shared among subscribers. In this article, we’ll break down the differences with examples and show how to work with each type effectively. Cold Observables: Fresh Data for Every Subscriber A cold observable is one where the underlying data is created inside the observable. This means that each subscription starts a new execution of the observable, producing unique values for each subscriber. Example of a Cold Observable import { Observable } from 'rxjs'; const coldObservable = new Observable(observer => { const randomNum = Math.random(); // Generates a new number per subscription observer.next(randomNum); observer.complete(); }); coldObservable.subscribe(value => console.log('Subscriber 1:', value)); coldObservable.subscribe(value => console.log('Subscriber 2:', value)); // Output: // Subscriber 1: 0.645732 // Subscriber 2: 0.927384 Each subscriber gets a different random number because the observable generates a new value each time someone subscribes. Hot Observables: Shared Data Among Subscribers A hot observable is one where the data is generated outside the observable. All subscribers receive the same data and share execution, preventing redundant computations. Example of a Hot Observable import { Observable } from 'rxjs'; const sharedRandomNum = Math.random(); const hotObservable = new Observable(observer => { observer.next(sharedRandomNum); observer.complete(); }); hotObservable.subscribe(value => console.log('Subscriber 1:', value)); hotObservable.subscribe(value => console.log('Subscriber 2:', value)); // Output: // Subscriber 1: 0.645732 // Subscriber 2: 0.645732 Since the random number is generated before the observable is created, all subscribers receive the same value. Making a Cold Observable Hot with publish() Instead of generating data externally, we can use the publish() operator to convert a cold observable into a hot one. import { Observable } from 'rxjs'; import { publish } from 'rxjs/operators'; const coldObservable = new Observable(observer => { const randomNum = Math.random(); observer.next(randomNum); observer.complete(); }); const hotObservable = coldObservable.pipe(publish()); hotObservable.subscribe(value => console.log('Subscriber 1:', value)); hotObservable.subscribe(value => console.log('Subscriber 2:', value)); hotObservable.connect(); // Ensures the observable emits shared values // Output: // Subscriber 1: 0.845291 // Subscriber 2: 0.845291 Calling connect() ensures that the observable emits data only once, making it behave like a hot observable. Completing Observables & Avoiding Memory Leaks Observables should be properly completed to avoid potential memory leaks, especially when dealing with continuous streams. Example: Completing an Observable Automatically Using finally(), we can detect when an observable completes: import { timer } from 'rxjs'; import { finalize } from 'rxjs/operators'; timer(1000).pipe( finalize(() => console.log('Observable completed')) ).subscribe(() => console.log('Emitted value')); Example: Manually Unsubscribing For infinite observables like interval(), we must manually unsubscribe to prevent leaks: import { interval } from 'rxjs'; const subscription = interval(1000).subscribe(value => console.log(value)); setTimeout(() => { subscription.unsubscribe(); console.log('Unsubscribed from observable'); }, 3000); After 3 seconds, the subscription stops receiving values, freein

I recently came across MkDocs-Material by Martin Donath, a fantastic open-source project with over 22k GitHub stars.
It’s an incredible contribution to the community, making documentation hosting effortless.
While exploring it, I got curious about how such a large project achieves reactiveness.
The stack is mostly HTML, SCSS, Preact, RxJS, and a few workers, and I saw this as the perfect opportunity to dive into RxJS—especially how it utilizes Observables and other advanced patterns.
I wrote an article on multicast yesterday, check it out:

Multicast & Chill: Getting Started with RxJS - III
Athreya aka Maneshwar ・ Feb 13

Operators & Chill: Getting Started with RxJS - II
Athreya aka Maneshwar ・ Feb 12

Observables & Chill: Getting Started with RxJS - I
Athreya aka Maneshwar ・ Feb 11
RxJS observables come in two flavors: hot and cold.
Understanding the difference between them is crucial when working with reactive programming, as it affects how data is produced and shared among subscribers.
In this article, we’ll break down the differences with examples and show how to work with each type effectively.
Cold Observables: Fresh Data for Every Subscriber
A cold observable is one where the underlying data is created inside the observable.
This means that each subscription starts a new execution of the observable, producing unique values for each subscriber.
Example of a Cold Observable
import { Observable } from 'rxjs';
const coldObservable = new Observable(observer => {
const randomNum = Math.random(); // Generates a new number per subscription
observer.next(randomNum);
observer.complete();
});
coldObservable.subscribe(value => console.log('Subscriber 1:', value));
coldObservable.subscribe(value => console.log('Subscriber 2:', value));
// Output:
// Subscriber 1: 0.645732
// Subscriber 2: 0.927384
Each subscriber gets a different random number because the observable generates a new value each time someone subscribes.
Hot Observables: Shared Data Among Subscribers
A hot observable is one where the data is generated outside the observable.
All subscribers receive the same data and share execution, preventing redundant computations.
Example of a Hot Observable
import { Observable } from 'rxjs';
const sharedRandomNum = Math.random();
const hotObservable = new Observable(observer => {
observer.next(sharedRandomNum);
observer.complete();
});
hotObservable.subscribe(value => console.log('Subscriber 1:', value));
hotObservable.subscribe(value => console.log('Subscriber 2:', value));
// Output:
// Subscriber 1: 0.645732
// Subscriber 2: 0.645732
Since the random number is generated before the observable is created, all subscribers receive the same value.
Making a Cold Observable Hot with publish()
Instead of generating data externally, we can use the publish()
operator to convert a cold observable into a hot one.
import { Observable } from 'rxjs';
import { publish } from 'rxjs/operators';
const coldObservable = new Observable(observer => {
const randomNum = Math.random();
observer.next(randomNum);
observer.complete();
});
const hotObservable = coldObservable.pipe(publish());
hotObservable.subscribe(value => console.log('Subscriber 1:', value));
hotObservable.subscribe(value => console.log('Subscriber 2:', value));
hotObservable.connect(); // Ensures the observable emits shared values
// Output:
// Subscriber 1: 0.845291
// Subscriber 2: 0.845291
Calling connect()
ensures that the observable emits data only once, making it behave like a hot observable.
Completing Observables & Avoiding Memory Leaks
Observables should be properly completed to avoid potential memory leaks, especially when dealing with continuous streams.
Example: Completing an Observable Automatically
Using finally()
, we can detect when an observable completes:
import { timer } from 'rxjs';
import { finalize } from 'rxjs/operators';
timer(1000).pipe(
finalize(() => console.log('Observable completed'))
).subscribe(() => console.log('Emitted value'));
Example: Manually Unsubscribing
For infinite observables like interval()
, we must manually unsubscribe to prevent leaks:
import { interval } from 'rxjs';
const subscription = interval(1000).subscribe(value => console.log(value));
setTimeout(() => {
subscription.unsubscribe();
console.log('Unsubscribed from observable');
}, 3000);
After 3 seconds, the subscription stops receiving values, freeing resources.
Conclusion
- Cold observables generate new data for each subscriber.
- Hot observables share data among subscribers.
- You can convert a cold observable into a hot one using
publish()
andconnect()
. - Always handle completion and unsubscribing to prevent memory leaks.
Understanding these concepts will help you build efficient and scalable reactive applications using RxJS!
I’ll be sharing more learnings, so stick around/follow for more deep dives into RxJS and beyond!