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 🙏

© 2025 – Pkg Stats / Ryan Hefner

react-dev-debugger

v1.0.2

Published

Visualize, debug, and time-travel through React state changes — instantly.

Downloads

279

Readme

React Dev Debugger


🎯 Problems This Package Solves

  • "Why did this component re-render?" — See exactly what changed
  • "Which state change caused this bug?" — Full timeline with diffs
  • "Where is this state coming from?" — Visual dependency graph
  • "How did my app reach this state?" — Time-travel debugging
  • "What dependencies are connected?" — Component-state relationships

✨ Features

| Feature | Description | |---------|-------------| | 📜 State Timeline | Chronological list of all state updates with before/after snapshots | | 🔗 Dependency Graph | Interactive visualization of component-state-context relationships | | ⏪ Time-Travel | Step backward/forward through state history | | 🔍 Why-Did-Update | Inspector showing exactly what caused re-renders | | ⚡ Performance Metrics | Render counts, durations, and slow render detection | | 🎨 Beautiful UI | Dark-themed floating panel with keyboard shortcuts | | 📦 Store Adapters | Built-in support for Redux, Zustand, Jotai, Recoil, Valtio | | 🚀 Zero-Config | Just import and go! |

📦 Installation

npm install react-dev-debugger
# or
yarn add react-dev-debugger
# or
pnpm add react-dev-debugger

� Documentation

For comprehensive guides, examples, and API documentation, visit:

🌐 https://react-dev-debugger.vercel.app/

�🚀 Quick Start

Zero-Config Setup

Add a single import at the top of your app's entry point:

// src/index.tsx or src/main.tsx
import 'react-dev-debugger/dev';
import React from 'react';
import ReactDOM from 'react-dom/client';
import App from './App';

ReactDOM.createRoot(document.getElementById('root')!).render(<App />);

That's it! The debugger panel will appear in the bottom-right corner.

Using Tracked Hooks

Replace your hooks with tracked versions to see state changes in the debugger:

import { useTrackedState, useTrackedReducer } from 'react-dev-debugger';

function Counter() {
  // Regular useState → useTrackedState
  const [count, setCount] = useTrackedState(0, 'counter');
  
  return (
    <button onClick={() => setCount(c => c + 1)}>
      Count: {count}
    </button>
  );
}

📚 API Reference

Tracked Hooks

useTrackedState<T>(initialValue, debugLabel?)

Drop-in replacement for useState with automatic tracking.

const [value, setValue] = useTrackedState(initialValue, 'myLabel');

useTrackedReducer<S, A>(reducer, initialState, initializer?, debugLabel?)

Drop-in replacement for useReducer with action tracking.

const [state, dispatch] = useTrackedReducer(reducer, initialState, undefined, 'myReducer');

useTrackedContext<T>(context, debugLabel?)

Context consumer that tracks value changes.

const theme = useTrackedContext(ThemeContext, 'theme');

useWhyDidYouUpdate(componentName, props)

Debug hook that logs why a component re-rendered.

function MyComponent(props) {
  useWhyDidYouUpdate('MyComponent', props);
  return <div>{props.name}</div>;
}

useRenderTracking(componentName?)

Track render performance for a component.

const { renderCount, lastDuration } = useRenderTracking('ExpensiveList');

Global API

Access via window.__REACT_STATE_DEBUGGER__:

// Time travel
window.__REACT_STATE_DEBUGGER__.goTo(5);      // Go to update #5
window.__REACT_STATE_DEBUGGER__.stepBack();    // Step back one update
window.__REACT_STATE_DEBUGGER__.stepForward(); // Step forward
window.__REACT_STATE_DEBUGGER__.pause();       // Pause live updates
window.__REACT_STATE_DEBUGGER__.resume();      // Resume live mode

// Data access
window.__REACT_STATE_DEBUGGER__.getUpdates();  // Get all updates
window.__REACT_STATE_DEBUGGER__.export();      // Export as JSON
window.__REACT_STATE_DEBUGGER__.clear();       // Clear all data

Keyboard Shortcuts

| Shortcut | Action | |----------|--------| | Ctrl/Cmd + Shift + D | Toggle debugger panel |

🔌 Store Adapters

Redux

import { createStore } from 'redux';
import { createReduxAdapter } from 'react-dev-debugger/adapters/redux';

const store = createStore(reducer);

if (process.env.NODE_ENV !== 'production') {
  createReduxAdapter(store, 'MyStore');
}

Zustand

import { create } from 'zustand';
import { createZustandAdapter } from 'react-dev-debugger/adapters/zustand';

const useStore = create((set) => ({
  count: 0,
  increment: () => set((state) => ({ count: state.count + 1 })),
}));

if (process.env.NODE_ENV !== 'production') {
  createZustandAdapter(useStore, 'CounterStore');
}

Jotai

import { createStore, atom } from 'jotai';
import { createJotaiAdapter } from 'react-dev-debugger/adapters/jotai';

const store = createStore();
const countAtom = atom(0);
countAtom.debugLabel = 'count';

if (process.env.NODE_ENV !== 'production') {
  createJotaiAdapter(store, [countAtom]);
}

Valtio

import { proxy, subscribe, snapshot } from 'valtio';
import { createValtioAdapter } from 'react-dev-debugger/adapters/valtio';

const state = proxy({ count: 0 });

if (process.env.NODE_ENV !== 'production') {
  createValtioAdapter(state, subscribe, snapshot, 'Counter');
}

💡 Examples

Complete App Example

// App.tsx
import 'react-dev-debugger/dev';
import { useTrackedState, useTrackedReducer, useTrackedContext } from 'react-dev-debugger';
import { createContext } from 'react';

// Context
const ThemeContext = createContext('light');

// Reducer
const counterReducer = (state, action) => {
  switch (action.type) {
    case 'increment': return { count: state.count + 1 };
    case 'decrement': return { count: state.count - 1 };
    default: return state;
  }
};

function Counter() {
  const [state, dispatch] = useTrackedReducer(
    counterReducer,
    { count: 0 },
    undefined,
    'counter'
  );
  
  return (
    <div>
      <p>Count: {state.count}</p>
      <button onClick={() => dispatch({ type: 'increment' })}>+</button>
      <button onClick={() => dispatch({ type: 'decrement' })}>-</button>
    </div>
  );
}

function UserProfile() {
  const [user, setUser] = useTrackedState(
    { name: 'John', age: 25 },
    'user'
  );
  const theme = useTrackedContext(ThemeContext, 'theme');
  
  return (
    <div style={{ background: theme === 'dark' ? '#333' : '#fff' }}>
      <p>Name: {user.name}</p>
      <button onClick={() => setUser({ ...user, age: user.age + 1 })}>
        Birthday!
      </button>
    </div>
  );
}

export default function App() {
  const [theme, setTheme] = useTrackedState('light', 'theme');
  
  return (
    <ThemeContext.Provider value={theme}>
      <button onClick={() => setTheme(t => t === 'light' ? 'dark' : 'light')}>
        Toggle Theme
      </button>
      <Counter />
      <UserProfile />
    </ThemeContext.Provider>
  );
}

❓ FAQ

Is it safe for production?

Yes! The debugger automatically disables itself in production builds:

  • Zero runtime overhead
  • No code included in production bundle (when using /dev entry)
  • Tree-shakable exports

Does it access React internals?

No. This package follows React's public APIs only:

  • No monkey-patching
  • No Fiber manipulation
  • No private React APIs
  • Works with any React 16.8+ version

How does time-travel work?

The debugger captures snapshots of tracked state at regular intervals. When you step through history, it restores these snapshots. Note: Only state registered via tracked hooks or store adapters can be restored.

Will it slow down my app?

The performance impact is minimal in development:

  • State cloning is optimized
  • UI updates are debounced
  • Timeline is capped at 1000 entries
  • Production builds have zero overhead

Does it work with SSR?

Yes! The debugger only runs in the browser and automatically skips initialization during SSR.

Comparison with Redux DevTools

| Feature | React Dev Debugger | Redux DevTools | |---------|-------------------|----------------| | Redux support | ✅ Via adapter | ✅ Native | | useState tracking | ✅ Built-in | ❌ Not supported | | useReducer tracking | ✅ Built-in | ❌ Not supported | | Context tracking | ✅ Built-in | ❌ Not supported | | Zustand/Jotai/etc | ✅ Via adapters | ❌ Limited | | Dependency graph | ✅ Yes | ❌ No | | Zero-config | ✅ One import | ❌ Requires setup | | Browser extension | ❌ In-app panel | ✅ Browser extension |

🛠 Advanced Configuration

Manual Panel Mounting

import { Panel, getStateTracker } from 'react-dev-debugger';

function MyDebugger() {
  return <Panel initialTab="timeline" initialMinimized={false} />;
}

Custom Adapters

import { getStateTracker } from 'react-dev-debugger';

const tracker = getStateTracker();

// Track custom state changes
tracker.trackUpdate(
  'custom-id',
  'CustomComponent',
  'external',
  0,
  prevValue,
  nextValue,
  { label: 'MyCustomState' }
);

� Links

�📄 License

MIT © Your Name