This content originally appeared on DEV Community and was authored by vetriselvan Panneerselvam
Hey Devs,
Welcome back to another weekly deep dive!
This time, we’re exploring an incredibly useful and powerful concept in RxJS — the ReplaySubject
.
In our previous posts, we covered how to pass values asynchronously using Subject
and BehaviorSubject
. Today, we’ll focus on how ReplaySubject
differs and why it’s helpful when you want new subscribers to receive previously emitted values.
What is a ReplaySubject?
A ReplaySubject
is similar to a BehaviorSubject
, but with a twist — it can replay multiple previously emitted values to new subscribers, not just the latest one.
Syntax:
const replaySubject = new ReplaySubject(2);
Here, the 2
represents the buffer size — meaning new subscribers will receive the last 2 emitted values upon subscription.
Note: Unlike
BehaviorSubject
,ReplaySubject
does not require an initial value. If you subscribe before emitting any values, you’ll receive nothing.
Example in Angular
Let’s take a look at a working example where we use ReplaySubject
in an Angular app.
HTML:
<h3>ReplaySubject</h3>
<div class="text-wrapper">
<div class="text">
<textarea
class="textarea-modern"
[(ngModel)]="replaySubjectText"
name="replaySubject"
id="replaySubject"
></textarea>
<div class="btn-container">
<button class="btn-ghost" (click)="sendReplaySubject()" type="button">Send</button>
<button class="btn-ghost" (click)="addReplaySubject()" type="button">Add</button>
</div>
</div>
<div class="example">
@for (item of replaySubjectArray(); track $index) {
@let data = replayObservable | async;
<div class="card">
<div class="index">{{$index}}</div>
@if (data && data.length > 0) {
@for (item of data; track item; let i = $index) {
<div class="card">
<div class="index">{{i}}</div>
<div class="data">{{item | json}}</div>
</div>
}
} @else {
<div class="data">{{data | json}}</div>
}
</div>
}
</div>
</div>
TypeScript:
import { Component, model, OnInit, signal } from '@angular/core';
import { CommonModule } from '@angular/common';
import { ReplaySubject, scan } from 'rxjs';
import { FormsModule } from '@angular/forms';
@Component({
selector: 'app-rxjs-operator',
imports: [CommonModule, FormsModule],
templateUrl: './rxjs-operator.html',
styleUrl: './rxjs-operator.scss',
})
export class RxjsOperator implements OnInit {
replaySubject = new ReplaySubject<string>(2);
replayObservable = this.replaySubject.asObservable().pipe(
scan((acc: string[], curr: string) => [...acc, curr], [])
);
replaySubjectText = model<string>('');
replaySubjectArray = signal<number[]>([0]);
ngOnInit(): void {}
sendReplaySubject() {
this.replaySubject.next(this.replaySubjectText());
}
addReplaySubject() {
this.replaySubjectArray.update((prev) => [...prev, 1]);
}
}
Use Case Breakdown
Case 1: Initial Load
When the component loads, the ReplaySubject
is subscribed to, but no values have been emitted yet — so the UI displays null
or empty output.
Refer to the screenshot showing the initial state (empty or
null
output).
Case 2: Emitting New Values
When you enter a value in the textarea and click the Send button, that value is emitted via replaySubject.next()
. We use the scan
operator to accumulate emitted values for display.
We’ll cover
scan
in more detail in an upcoming blog post!
Case 3: New Subscribers, Old Data
After emitting more than two values (our buffer size is 2), clicking the Add button creates a new subscriber. That new subscriber immediately receives the last two emitted values — this is the magic of ReplaySubject
.
Any newly emitted values will be received by all active subscribers, just like with a regular Subject
.
Under the Hood
Internally, ReplaySubject
is a class that extends Subject
. Here’s a simplified look at how it’s structured:
export class ReplaySubject<T> extends Subject<T> {
constructor(
private _bufferSize = Infinity,
private _windowTime = Infinity,
private _timestampProvider: TimestampProvider = dateTimestampProvider
) {
super();
}
next(value: T): void {
// This method is overridden to buffer the emitted values
// and replay them to new subscribers based on the buffer size
}
}
The key difference lies in the overridden next()
method. It stores emitted values in memory and re-emits them to new subscribers — depending on the configured bufferSize
and windowTime
.
This makes ReplaySubject
perfect when you want late subscribers to catch up on a stream’s most recent activity, rather than starting fresh.
Conclusion
That wraps up our deep dive into ReplaySubject
!
To summarize:
- It stores and replays multiple past values to new subscribers.
- No need for an initial value.
- Super useful when late subscribers need context.
Next up, we’ll explore AsyncSubject and see how it fits into the RxJS family.
Got questions or use cases you want to share? Drop a comment below! Let’s discuss more Angular magic.
Author: Vetriselvan
Frontend Developer |
Code Enthusiast |
Lifelong Learner |
Tech Blogger |
Freelance Developer
This content originally appeared on DEV Community and was authored by vetriselvan Panneerselvam