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 observables yesterday, check it out:
![lovestaco](https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1002302%2F5233b7df-6ee3-46b2-b8d7-1fafe103e8a3.jpg)
Observables & Chill: Getting Started with RxJS
Athreya aka Maneshwar ・ Feb 11
Now, let's dive deeper into the real game-changers: RxJS Operators.
1. map
The map
operator is your go-to when you need to transform data.
Think of it like JavaScript's Array.map
, but for Observables.
Example:
import { of } from 'rxjs';
import { map } from 'rxjs/operators';
const jsonStr = '{ "greetType": "Hi", "familyMember": "Mom" }';
of(jsonStr)
.pipe(
map(json => JSON.parse(json))
)
.subscribe(obj => {
console.log(obj.greetType);
console.log(obj.familyMember);
});
// Output:
// Hi
// Mom
Here, we're transforming a JSON string into a usable JavaScript object.
Perfect when handling API responses.
2. tap
tap
lets you perform side effects without affecting the actual data stream.
It's great for debugging.
Example:
import { of } from 'rxjs';
import { tap, map } from 'rxjs/operators';
of('rxjs')
.pipe(
tap(value => console.log(`Original: ${value}`)),
map(value => value.toUpperCase()),
tap(value => console.log(`Transformed: ${value}`))
)
.subscribe();
You’ll see both the original and transformed values in the console.
Super handy for peeking into the data flow.
3. filter
filter
does exactly what you think: it filters data based on a condition.
Example:
import { from } from 'rxjs';
import { filter } from 'rxjs/operators';
from([1, 2, 3, 4, 5])
.pipe(
filter(num => num % 2 === 0)
)
.subscribe(console.log);
// Output:
// 2, 4
Only even numbers make it through.
It's like the bouncer of your data stream.
4. debounceTime
& throttleTime
Both operators control the rate of emitted values but behave differently:
-
debounceTime
emits the last value after a delay. -
throttleTime
emits the first value, then ignores subsequent values for the set time.
Example:
import { fromEvent } from 'rxjs';
import { debounceTime, throttleTime } from 'rxjs/operators';
const input = document.getElementById('search');
fromEvent(input, 'input')
.pipe(
debounceTime(500)
)
.subscribe(() => console.log('Debounced input:', input.value));
fromEvent(input, 'input')
.pipe(
throttleTime(1000)
)
.subscribe(() => console.log('Throttled input:', input.value));
Try typing fast to see the difference.
debounceTime
waits until you pause; throttleTime
logs intermittently.
5. scan
scan
accumulates values over time, similar to reduce
in JavaScript.
Example:
import { fromEvent } from 'rxjs';
import { map, scan } from 'rxjs/operators';
fromEvent(document, 'click')
.pipe(
map(() => 1),
scan((acc, curr) => acc + curr, 0)
)
.subscribe(count => console.log(`Total clicks: ${count}`));
Every click increments the total count.
Simple yet powerful for cumulative tasks.
6. switchMap
switchMap
is ideal when you need to cancel previous requests and switch to a new one.
Example:
import { fromEvent, interval } from 'rxjs';
import { switchMap } from 'rxjs/operators';
fromEvent(document, 'click')
.pipe(
switchMap(() => interval(1000))
)
.subscribe(console.log);
Clicking resets the interval.
Perfect for scenarios like search suggestions or live data feeds.
7. takeUntil
takeUntil
stops emissions when another Observable emits a value.
Example:
import { interval, timer } from 'rxjs';
import { takeUntil } from 'rxjs/operators';
const source$ = interval(500);
const stopper$ = timer(3000);
source$
.pipe(
takeUntil(stopper$)
)
.subscribe({
next: console.log,
complete: () => console.log('Completed!')
});
The interval runs until the timer fires after 3 seconds, then completes automatically.
8. takeWhile
takeWhile
emits values as long as a condition is true.
Example:
import { from } from 'rxjs';
import { takeWhile } from 'rxjs/operators';
from(["Alice", "Bob", "Charlie", "Doug", "Eve"])
.pipe(
takeWhile(name => name !== "Doug")
)
.subscribe(console.log);
It stops emitting once it hits "Doug".
Great for conditional data flows.
Wrapping Up
RxJS operators are the real magic behind reactive programming.
They help you transform, filter, combine, and control data streams effortlessly.
Experiment with these operators, combine them, and soon you'll be orchestrating complex async workflows like a pro.
I’ll be sharing more learnings, so stick around/follow for more deep dives into RxJS and beyond! 🚀
While exploring mkdocs-material implementation, I've been learning how to adapt these techniques for LiveAPI, a product I've been passionately working on for quite a while.
With LiveAPI, you can quickly generate interactive API documentation that allows users to execute APIs directly from the browser.
If you’re tired of manually creating docs for your APIs, this tool might just make your life easier.
Top comments (0)