Beautiful macOS app for debugging React Query in ANY React-based application (Mobile, Web, TV, VR). Monitor queries across devices in real-time with a gorgeous native interface. Features device management, query visualization, state simulation, and automatic production safety. Works with React Native, React Web, Next.js, Expo, and any platform wher
Enhanced developer tools for React Native applications, supporting React Query DevTools and device storage monitoring with a beautiful native interface.
https://github.com/user-attachments/assets/5c0c5748-e031-427a-8ebf-9c085434e8ba
https://github.com/LovesWorking/RN-Dev-Tools-Example
https://github.com/LovesWorking/react-native-react-query-devtools
โ ๏ธ Important: The desktop app has currently only been tested on Apple Silicon Macs (M1/M2/M3).
If you encounter issues on Intel-based Macs, please open an issue
and weโll work together to fix it.
The easiest way to connect your React application to the DevTools is by installing the npm package:
# Using npm
npm install --save-dev react-query-external-sync socket.io-client
# Using yarn
yarn add -D react-query-external-sync socket.io-client
# Using pnpm (recommended)
pnpm add -D react-query-external-sync socket.io-client
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
import { useSyncQueriesExternal } from "react-query-external-sync";
// Import Platform for React Native or use other platform detection for web/desktop
import { Platform } from "react-native";
import AsyncStorage from "@react-native-async-storage/async-storage";
import * as SecureStore from "expo-secure-store";
import { storage } from "./mmkv"; // Your MMKV instance
// Create your query client
const queryClient = new QueryClient();
function App() {
return (
<QueryClientProvider client={queryClient}>
<AppContent />
</QueryClientProvider>
);
}
function AppContent() {
// Set up the sync hook - automatically disabled in production!
useSyncQueriesExternal({
queryClient,
socketURL: "http://localhost:42831", // Default port for React Native DevTools
deviceName: Platform?.OS || "web", // Platform detection
platform: Platform?.OS || "web", // Use appropriate platform identifier
deviceId: Platform?.OS || "web", // Use a PERSISTENT identifier (see note below)
extraDeviceInfo: {
// Optional additional info about your device
appVersion: "1.0.0",
// Add any relevant platform info
},
enableLogs: false,
envVariables: {
NODE_ENV: process.env.NODE_ENV,
// Add any private environment variables you want to monitor
// Public environment variables are automatically loaded
},
// Storage monitoring with CRUD operations
mmkvStorage: storage, // MMKV storage for ['#storage', 'mmkv', 'key'] queries + monitoring
asyncStorage: AsyncStorage, // AsyncStorage for ['#storage', 'async', 'key'] queries + monitoring
secureStorage: SecureStore, // SecureStore for ['#storage', 'secure', 'key'] queries + monitoring
secureStorageKeys: [
"userToken",
"refreshToken",
"biometricKey",
"deviceId",
], // SecureStore keys to monitor
});
// Your app content
return <YourApp />;
}
When testing on real devices connected to your local network, youโll need to use your host machineโs IP address instead of localhost
. Hereโs a helpful setup for Expo apps (contributed by ShoeBoom):
import Constants from "expo-constants";
import AsyncStorage from "@react-native-async-storage/async-storage";
import * as SecureStore from "expo-secure-store";
import { storage } from "./mmkv"; // Your MMKV instance
// Get the host IP address dynamically
const hostIP =
Constants.expoGoConfig?.debuggerHost?.split(`:`)[0] ||
Constants.expoConfig?.hostUri?.split(`:`)[0];
function AppContent() {
useSyncQueriesExternal({
queryClient,
socketURL: `http://${hostIP}:42831`, // Use local network IP
deviceName: Platform?.OS || "web",
platform: Platform?.OS || "web",
deviceId: Platform?.OS || "web",
extraDeviceInfo: {
appVersion: "1.0.0",
},
enableLogs: false,
envVariables: {
NODE_ENV: process.env.NODE_ENV,
},
// Storage monitoring
mmkvStorage: storage,
asyncStorage: AsyncStorage,
secureStorage: SecureStore,
secureStorageKeys: ["userToken", "refreshToken"],
});
return <YourApp />;
}
Note: For optimal connection, launch DevTools before starting your application.
['#storage', 'mmkv', 'key']
React Native DevTools works with any React-based application, regardless of platform:
If your platform can run React and connect to a socket server, it will work with these DevTools!
React Native DevTools now includes powerful storage monitoring capabilities with full CRUD operations:
['#storage', 'mmkv', 'keyName']
// In your app, use React Query to interact with storage
import { useQuery, useMutation, useQueryClient } from "@tanstack/react-query";
// Read from MMKV storage
const { data: userData } = useQuery({
queryKey: ["#storage", "mmkv", "user"],
// Data will be automatically synced with DevTools
});
// Write to AsyncStorage
const queryClient = useQueryClient();
const updateAsyncStorage = useMutation({
mutationFn: async ({ key, value }) => {
await AsyncStorage.setItem(key, JSON.stringify(value));
// Invalidate to trigger sync
queryClient.invalidateQueries(["#storage", "async", key]);
},
});
The useSyncQueriesExternal
hook accepts the following options:
Option | Type | Required | Description |
---|---|---|---|
queryClient |
QueryClient | Yes | Your React Query client instance |
socketURL |
string | Yes | URL of the socket server (e.g., โhttp://localhost:42831โ) |
deviceName |
string | Yes | Human-readable name for your device |
platform |
string | Yes | Platform identifier (โiosโ, โandroidโ, โwebโ, โmacosโ, โwindowsโ, etc.) |
deviceId |
string | Yes | Unique identifier for your device |
extraDeviceInfo |
object | No | Additional device metadata to display in DevTools |
enableLogs |
boolean | No | Enable console logging for debugging (default: false) |
envVariables |
object | No | Private environment variables to sync with DevTools (public vars are auto-loaded) |
mmkvStorage |
MmkvStorage | No | MMKV storage instance for real-time monitoring |
asyncStorage |
AsyncStorage | No | AsyncStorage instance for polling-based monitoring |
secureStorage |
SecureStore | No | SecureStore instance for secure data monitoring |
secureStorageKeys |
string[] | No | Array of SecureStore keys to monitor (required if using secureStorage) |
React Native DevTools is actively being developed with exciting features on the roadmap:
Stay tuned for updates!
I welcome contributions! See Development Guide for details on:
Having issues? Check these common solutions:
App Not Connecting
socketURL
is correctly pointing to localhost:42831useSyncQueriesExternal
hook is properly implementedApp Not Starting
Socket Connection Issues
enableLogs: true
for any error messagesData Not Syncing
queryClient
instanceenableLogs: true
to see connection informationDevice ID Issues
deviceId
is persistent (see below)Storage Monitoring Issues
secureStorageKeys
array is providedenableLogs: true
to see storage sync informationThe deviceId
parameter must be persistent across app restarts and re-renders. Using a value that changes (like Date.now()
) will cause each render to be treated as a new device.
Recommended approaches:
// Simple approach for single devices
deviceId: Platform.OS, // Works if you only have one device per platform
// Better approach for multiple simulators/devices of same type
// Using AsyncStorage, MMKV, or another storage solution
const [deviceId, setDeviceId] = useState(Platform.OS);
useEffect(() => {
const loadOrCreateDeviceId = async () => {
// Try to load existing ID
const storedId = await AsyncStorage.getItem('deviceId');
if (storedId) {
setDeviceId(storedId);
} else {
// First launch - generate and store a persistent ID
const newId = `${Platform.OS}-${Date.now()}`;
await AsyncStorage.setItem('deviceId', newId);
setDeviceId(newId);
}
};
loadOrCreateDeviceId();
}, []);
For more detailed troubleshooting, see our Development Guide.
MIT
Made with โค๏ธ by LovesWorking