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

mobx-use-react

v0.1.1

Published

React hooks for explicit MobX observation without wrapping components in observer.

Readme

mobx-use-react

React hooks for explicit MobX observation without wrapping components in observer.

mobx-use-react is designed for React 19+, MobX 6, web, and React Native. The main entrypoint configures React batching automatically, matching the default ergonomics of mobx-react-lite.

Installation

bun add mobx-use-react mobx react

For web apps, install react-dom. For React Native apps, install react-native.

useObservable

Read a single observable property:

import { useObservable } from "mobx-use-react";

function Counter({ store }: { store: { count: number } }) {
  const count = useObservable(store, "count");

  return <Text>{count}</Text>;
}

Read a derived value:

function UserLabel({ user }: { user: UserStore }) {
  const label = useObservable(() => `${user.firstName} ${user.lastName}`);

  return <Text>{label}</Text>;
}

Only observable reads inside useObservable are tracked. Reads elsewhere in the component behave like normal JavaScript reads.

Usage Guide

MobX tracks the exact observable properties read by each useObservable call. React still rerenders at component boundaries, so place subscriptions in the smallest component that actually needs the value:

function UserRow({ user }: { user: UserStore }) {
  return (
    <View>
      <UserName user={user} />
      <UserPresence user={user} />
    </View>
  );
}

function UserName({ user }: { user: UserStore }) {
  const name = useObservable(user, "name");

  return <Text>{name}</Text>;
}

function UserPresence({ user }: { user: UserStore }) {
  const isOnline = useObservable(user, "isOnline");

  return <StatusDot online={isOnline} />;
}

Pass observable objects down and dereference them late. Reading user.name in UserName lets a name change rerender UserName without rerendering the parent that passed user.

Use direct property reads for simple values:

const name = useObservable(user, "name");
const email = useObservable(user, "email");
const isOnline = useObservable(user, "isOnline");

Use a selector when the component intentionally needs one derived or grouped view:

const displayName = useObservable(() => `${user.firstName} ${user.lastName}`);

For large collections, keep list mapping in a dedicated component and track the list structure there. Use stable keys so React can preserve rows across inserts, removes, and reorders:

function UserPanel({
  users,
  session,
}: {
  users: UserStore[];
  session: SessionStore;
}) {
  const title = useObservable(session, "title");

  return (
    <View>
      <Text>{title}</Text>
      <UserList users={users} />
    </View>
  );
}

function UserList({ users }: { users: UserStore[] }) {
  const rowUsers = useObservable(() => users.slice());

  return rowUsers.map((user) => <UserRow key={user.id} user={user} />);
}

When passing data into a component that does not call useObservable, pass plain values instead of expecting that child to track observables:

function UserBadge({ user }: { user: UserStore }) {
  const name = useObservable(user, "name");
  const avatarUrl = useObservable(user, "avatarUrl");

  return <ThirdPartyBadge name={name} avatarUrl={avatarUrl} />;
}

useObservableEffect

Use MobX tracking for side effects that should run when observables change, without requiring a React rerender:

import { useObservableEffect } from "mobx-use-react";

function UserSubscription({ store }: { store: SessionStore }) {
  useObservableEffect(
    () => store.userId,
    (userId) => {
      if (!userId) return;

      const subscription = subscribeToUser(userId);

      return () => {
        subscription.close();
      };
    },
    { fireImmediately: true },
  );

  return null;
}

useObservableEffect uses React for mount/unmount cleanup and MobX reaction for observable tracking. Cleanup returned by the effect runs before the next effect and on unmount.

If the selector captures React props or state that changes which observables are tracked, pass deps:

useObservableEffect(
  () => (showPrimary ? store.primaryUserId : store.secondaryUserId),
  (userId) => {
    analytics.identify(userId);
  },
  { fireImmediately: true, deps: [showPrimary] },
);

All MobX reaction options are supported, including fireImmediately, equals, delay, scheduler, name, onError, and signal.

Static Rendering

For SSR-style rendering where hooks should read values without subscribing:

import { enableStaticRendering } from "mobx-use-react";

enableStaticRendering(true);

Use isUsingStaticRendering() to inspect the current flag.

Batching

The main package configures MobX batching automatically:

import { useObservable } from "mobx-use-react";

Web bundlers use react-dom batching. React Native bundlers can use the package's react-native entry, which configures react-native batching.

MobX has one global reactionScheduler; if multiple libraries configure it, the last configuration wins. This is compatible with mobx-react-lite: both libraries configure MobX to use React's platform batch function, so importing both is fine.

API

useObservable<TObject, TKey extends keyof TObject>(
  source: TObject,
  key: TKey,
): TObject[TKey];

useObservable<TValue>(selector: () => TValue): TValue;

useObservableEffect<TValue>(
  selector: () => TValue,
  effect: (
    value: TValue,
    previousValue: TValue,
    reaction: IReactionPublic,
  ) => void | (() => void),
  options?: UseObservableEffectOptions<TValue>,
): void;

enableStaticRendering(enable: boolean): void;
isUsingStaticRendering(): boolean;

clearTimers() is exported for tests that need to flush abandoned-render cleanup fallback timers.