Jotai DevTools

+
UniversalWorks with Expo & React Native CLI

This tool works with both Expo and React Native CLI projects. Just install and go.

Atoms are small on purpose, which means a wrong value has usually travelled through four of them before it reaches the screen. Jotai's model gives you nothing to log — there is no store to inspect, and derived atoms recompute silently — so the usual approach is a useEffect that prints one atom at a time.

Buoy registers your atoms by name and shows their live values in one list, with a write history that puts prev → next on every change. You find the atom where the value went wrong, instead of the component where you noticed.

Atoms update as the app runs, applyPromo wipes cart.total, you read prev → next and the on-device diff, then Clear the log and the broken value is still sitting on Atoms.

Atoms update as the app runs

1 / 6

Not a video — this is the shipped tool on mock data. cart, auth.session, and user.profile update as every write lands — no wrappers, no polling.

9:41
Loading live demo…
Mock checkout atoms, real component — the same code that renders in your app. Step through the tour, or just start tapping.

Installation

npm install @buoy-gg/jotai

Setup

Call watchAtoms once at module scope — pass your Jotai store and a named map of your atoms. No wrappers, no middleware, no modifications to existing atoms.

tsx
import { getDefaultStore } from 'jotai';
import { watchAtoms } from '@buoy-gg/jotai';
import { countAtom } from './atoms/count';
import { authAtom } from './atoms/auth';
import { cartAtom } from './atoms/cart';

watchAtoms(getDefaultStore(), {
  countAtom,
  authAtom,
  cartAtom,
});
import { getDefaultStore } from 'jotai';
import { watchAtoms } from '@buoy-gg/jotai';
import { countAtom } from './atoms/count';
import { authAtom } from './atoms/auth';
import { cartAtom } from './atoms/cart';

watchAtoms(getDefaultStore(), {
  countAtom,
  authAtom,
  cartAtom,
});

That's it. Registered atoms automatically appear in the Jotai tool inside your FloatingDevTools menu.

Zero config required — Your existing atoms work as-is. No wrappers, no atomWithDevTools, nothing to change.


Atoms Tab

Browse all registered atoms and their live current value:

  • Atom Name — Color-coded for easy identification across both tabs
  • Value Typenumber, boolean, object, array · N, null, etc. at a glance
  • Live Value — Tap any atom to expand and see the full value tree, updated in real-time
  • Change Count — How many times this atom has changed this session
  • View History — Jump straight to the filtered event history for a single atom

Events Tab

Every atom change is captured with rich metadata:

  • Atom Name — Which atom changed, color-coded for quick identification
  • Value Transitionprev → next at a glance (e.g. 0 → 5, null → {name, email}, [2 items] → [3 items])
  • Category BadgeINIT (initial registration) or WRITE (subsequent update)
  • No Change — Flags writes that fired but didn't actually change the value
  • Timestamp — When the change occurred with relative time

Detail View

Tap any event to see three detailed tabs:

Change Tab

Atom name, timestamp, category badge, and a diff summary showing which object keys changed.

Value Tab

The full atom value after this change — collapsible JSON tree for objects, raw value for primitives.

Diff Tab

Side-by-side comparison of before and after — additions (green), removals (red), modifications (yellow). Choose between tree view or split view.


Using a Custom Store

If your app uses a <Provider> with a custom store, pass it directly:

tsx
import { createStore, Provider } from 'jotai';
import { watchAtoms } from '@buoy-gg/jotai';
import { countAtom, authAtom } from './atoms';

const myStore = createStore();

watchAtoms(myStore, { countAtom, authAtom });

export function App() {
  return (
    <Provider store={myStore}>
      <YourApp />
    </Provider>
  );
}
import { createStore, Provider } from 'jotai';
import { watchAtoms } from '@buoy-gg/jotai';
import { countAtom, authAtom } from './atoms';

const myStore = createStore();

watchAtoms(myStore, { countAtom, authAtom });

export function App() {
  return (
    <Provider store={myStore}>
      <YourApp />
    </Provider>
  );
}

Features

Atom Color Coding

Each atom gets a consistent color across the Atoms tab, Events tab, and detail views. Colors are assigned automatically based on atom name and persist for the session.

Value Transition at a Glance

Every event row shows prev → next so you immediately know what changed — no need to tap in for simple updates.

Atom History

Tap "view history" on any atom in the Atoms tab to see all events scoped to just that atom — useful for tracking a specific piece of state through a flow.

Search & Filter

Find events by atom name or value content. Add filter patterns to hide noisy atoms from both the Atoms and Events tabs simultaneously.

Copy to Clipboard

Export the full atoms snapshot or event history as JSON for bug reports, test fixtures, or sharing with teammates (Pro).

Recording Toggle

Pause atom capture when you need to focus, resume when ready.


What It Can't Do

Only atoms you register are visible. Jotai has no store to enumerate — atoms are module-level values — so Buoy shows what you pass to watchAtoms and nothing else. An atom you forgot to register is invisible, not missing.

Derived atoms are read-only. An atom computed from others has no setter, so Buoy captures it for inspection and refuses to write it (Atom "…" is read-only (derived) — cannot set). On restore, derived atoms recompute from their sources rather than being set back — which is the correct behaviour, and worth knowing before you expect a restore to pin one.

What's Next


FAQ

How do I inspect Jotai atoms in React Native?

Install @buoy-gg/jotai and register your atoms — they appear in the on-device browser with live values, write history, and diffs.