npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

rxjs-hook

v1.0.0

Published

A lightweight, hook-based bridge to manage RxJS observable subscriptions and state synchronization within React components.

Downloads

187

Readme

rxjs-hook

Subscribe to RxJS Observables and Subjects inside React components. No tearing, concurrent-safe (uses useSyncExternalStore under the hood).

If you know RxJS and React: pick the hook that matches your RxJS type; it returns the latest value (or a callback + stream). No magic.


Install

pnpm add rxjs-hook react rxjs
# npm install rxjs-hook react rxjs
# yarn add rxjs-hook react rxjs

Peer deps: react ^18 | ^19, rxjs ^7.


Which hook do I use?

| You have… | Use this hook | You get… | |------------------------|----------------------------|-----------------------------------| | BehaviorSubject<T> | useBehaviorSubject | T (always defined) | | Subject<T> | useSubject | T or undefined | | ReplaySubject<T> | useReplaySubject | T or undefined | | AsyncSubject<T> | useAsyncSubject | T or undefined (after complete)| | Observable<T> | useObservable | T (you pass initialValue) | | Side effects only | useSubscription | nothing (next/error/complete) | | Turn callbacks → stream | useObservableCallback | [callback, Subject<T>] |


State hooks (subscribe → current value)

These hooks subscribe to a stream and return the latest value. Component re-renders when the stream emits.

useBehaviorSubject

BehaviorSubject always has a current value. Hook returns it.

import { useBehaviorSubject } from "rxjs-hook";
import { BehaviorSubject } from "rxjs";

const count$ = new BehaviorSubject(0);

function Counter() {
  const count = useBehaviorSubject(count$);
  return <button onClick={() => count$.next(count + 1)}>{count}</button>;
}

useSubject

Subject has no initial value. Hook returns undefined until the first emission, then the latest value.

import { useSubject } from "rxjs-hook";
import { Subject } from "rxjs";

const events$ = new Subject<string>();

function Log() {
  const last = useSubject(events$);
  return <div>{last ?? "—"}</div>;
}

useReplaySubject

ReplaySubject replays N values to new subscribers. Hook returns the latest replayed value (or undefined before any emission).

import { useReplaySubject } from "rxjs-hook";
import { ReplaySubject } from "rxjs";

const state$ = new ReplaySubject<State>(1);

function App() {
  const state = useReplaySubject(state$);
  return state ? <View state={state} /> : null;
}

useAsyncSubject

AsyncSubject emits only when it completes, and only the last value (Promise-like). Hook returns that value after complete(), otherwise undefined.

import { useAsyncSubject } from "rxjs-hook";
import { AsyncSubject } from "rxjs";

const result$ = new AsyncSubject<Data>();
fetch(url).then((r) => r.json()).then((d) => { result$.next(d); result$.complete(); });

function DataView() {
  const data = useAsyncSubject(result$);
  return data ? <pre>{JSON.stringify(data)}</pre> : <span>Loading…</span>;
}

useObservable

Any Observable<T>. You must pass an initialValue; the hook returns it until the first emission, then the latest value.

import { useObservable } from "rxjs-hook";
import { interval, map } from "rxjs";

const ticks$ = interval(1000).pipe(map((n) => n + 1));

function Timer() {
  const tick = useObservable(ticks$, 0);
  return <span>{tick}</span>;
}

Side effects: useSubscription

Subscribe to an Observable and run next / error / complete. Does not drive React state. Use for logging, analytics, or triggering external logic.

import { useSubscription } from "rxjs-hook";
import { events$ } from "./events";

function Logger() {
  useSubscription(events$, {
    next: (e) => console.log(e),
    error: (err) => console.error(err),
    complete: () => console.log("done"),
  });
  return null;
}

Pass null or undefined as the first argument to “disable” the subscription.


Events → stream: useObservableCallback

Get a callback and a Subject. Call the callback (e.g. from onChange); values go into the Subject. Subscribe or .pipe() on it (debounce, map, etc.).

import { useObservableCallback, useSubscription } from "rxjs-hook";
import { debounceTime } from "rxjs";

function Search() {
  const [onChange, query$] = useObservableCallback<string>();

  useSubscription(query$.pipe(debounceTime(300)), {
    next: (q) => console.log("search:", q),
  });

  return <input onChange={(e) => onChange(e.target.value)} />;
}

Or combine with useObservable if you want the latest value in state:

const [onChange, query$] = useObservableCallback<string>();
const query = useObservable(query$.pipe(debounceTime(300)), "");
return <input value={query} onChange={(e) => onChange(e.target.value)} />;

Tree-shaking

Import from subpaths so only the hooks you use are bundled:

import { useBehaviorSubject } from "rxjs-hook/useBehaviorSubject";
import { useObservableCallback } from "rxjs-hook/useObservableCallback";
import { useSubscription } from "rxjs-hook/useSubscription";
// etc.

Publishing

Push a version tag to run the Publish to npm workflow (lint → test → build → publish):

pnpm version patch && git push && git push origin v0.0.3

Repo secret NPM_TOKEN (npm classic token with Publish scope) is required.


License

ISC