react-dev-debugger
v1.0.2
Published
Visualize, debug, and time-travel through React state changes — instantly.
Downloads
279
Maintainers
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 dataKeyboard 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
/deventry) - 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
- Documentation: https://react-dev-debugger.vercel.app/
- GitHub: https://github.com/princeofv/react-dev-debugger
- npm: https://www.npmjs.com/package/react-dev-debugger- Support: Buy Me a Coffee ☕
�📄 License
MIT © Your Name
