Experimental rewrite of React DevTools extension for debugging React applications with improved performance and multi-root support
npx @tessl/cli install tessl/npm-react-devtools-experimental@4.0.0React DevTools Experimental is an advanced debugging tool for React applications that provides deep inspection capabilities, performance profiling, and state modification features. This experimental rewrite offers improved performance through message batching, windowing, and reduced bridge traffic compared to legacy DevTools.
React DevTools Experimental provides different entry points for different contexts:
Backend Hook Installation:
import { installHook } from "react-devtools-experimental/src/hook";Backend Initialization:
import { initBackend } from "react-devtools-experimental/src/backend";
import Agent from "react-devtools-experimental/src/backend/agent";Frontend Initialization:
import { initDevTools } from "react-devtools-experimental/src/devtools";
import Store from "react-devtools-experimental/src/devtools/store";Bridge Communication:
import Bridge from "react-devtools-experimental/src/bridge";Setting up DevTools in a React application:
import { installHook } from "react-devtools-experimental/src/hook";
import { initBackend } from "react-devtools-experimental/src/backend";
import Agent from "react-devtools-experimental/src/backend/agent";
import Bridge from "react-devtools-experimental/src/bridge";
// 1. Install the global hook
const hook = installHook(window);
if (!hook) {
console.warn("React DevTools hook already installed");
return;
}
// 2. Create bridge with wall implementation
const wall = {
listen: (fn) => window.addEventListener('message', fn),
send: (event, payload) => window.postMessage({ event, payload }, '*')
};
const bridge = new Bridge(wall);
// 3. Initialize backend
const agent = new Agent();
agent.addBridge(bridge);
initBackend(hook, agent, window);
// 4. DevTools is now ready to detect and debug React applicationsBasic element inspection:
// Agent methods for element inspection
agent.selectElement({ id: elementId, rendererID: rendererID });
agent.inspectElement({ id: elementId, rendererID: rendererID });
agent.highlightElementInDOM({ id: elementId, rendererID: rendererID });React DevTools Experimental is built around several key architectural patterns:
Core hook system that enables DevTools integration with React applications through global hook installation and renderer detection.
function installHook(target: any): DevToolsHook | null;
interface DevToolsHook {
listeners: { [key: string]: Array<Function> };
rendererInterfaces: Map<number, RendererInterface>;
renderers: Map<number, ReactRenderer>;
emit: (event: string, data: any) => void;
on: (event: string, handler: Function) => void;
off: (event: string, handler: Function) => void;
sub: (event: string, handler: Function) => () => void;
inject: (renderer: ReactRenderer) => number | null;
getFiberRoots: (rendererID: number) => Set<Object>;
}Message passing system with automatic batching for efficient communication between DevTools contexts (extension, content script, injected script).
class Bridge extends EventEmitter {
constructor(wall: Wall);
send(event: string, payload: any, transferable?: Array<any>): void;
}
interface Wall {
listen: (fn: Function) => void;
send: (event: string, payload: any, transferable?: Array<any>) => void;
}Central backend coordinator that manages renderer interfaces, handles element inspection, state modification, and profiling operations.
class Agent extends EventEmitter {
addBridge(bridge: Bridge): void;
inspectElement(params: { id: number, rendererID: number }): void;
selectElement(params: { id: number, rendererID: number }): void;
highlightElementInDOM(params: { id: number, rendererID: number }): void;
startInspectingDOM(): void;
stopInspectingDOM(): void;
overrideContext(params: SetInParams): void;
overrideHookState(params: OverrideHookParams): void;
overrideProps(params: SetInParams): void;
overrideState(params: SetInParams): void;
startProfiling(): void;
stopProfiling(): void;
}
interface SetInParams {
id: number;
path: Array<string | number>;
rendererID: number;
value: any;
}
interface OverrideHookParams {
id: number;
hookID: number;
path: Array<string | number>;
rendererID: number;
value: any;
}Sophisticated data serialization system that handles complex objects, functions, React elements, and circular references for bridge transport.
const meta = {
name: Symbol('name');
type: Symbol('type');
inspected: Symbol('inspected');
meta: Symbol('meta');
proto: Symbol('proto');
};
function dehydrate(
data: Object,
cleaned: Array<Array<string>>,
path?: Array<string>,
level?: number
): string | Object;
function hydrate(data: Object, cleaned: Array<Array<string>>): Object;
function getDisplayNameForReactElement(element: React$Element<any>): string | null;Event-driven state management for DevTools UI with element tree management, profiling data caching, and Suspense integration.
class Store extends EventEmitter {
constructor(bridge: Bridge, config?: Config);
get hasProfilingData(): boolean;
get isProfiling(): boolean;
get numElements(): number;
get roots(): $ReadOnlyArray<number>;
get supportsProfiling(): boolean;
getElementAtIndex(index: number): Element | null;
getElementIDAtIndex(index: number): number | null;
getElementByID(id: number): Element | null;
getIndexOfElementID(id: number): number | null;
startProfiling(): void;
stopProfiling(): void;
}
interface Config {
supportsReloadAndProfile?: boolean;
supportsProfiling?: boolean;
}Comprehensive performance monitoring system with interaction tracking, commit analysis, and detailed timing data collection.
interface Interaction {
id: number;
name: string;
timestamp: number;
}
interface CommitDetails {
actualDurations: Array<number>;
commitIndex: number;
interactions: Array<Interaction>;
rootID: number;
}
interface ProfilingSummary {
commitDurations: Array<number>;
commitTimes: Array<number>;
initialTreeBaseDurations: Array<number>;
interactionCount: number;
rootID: number;
}React components for rendering the DevTools interface including element tree, profiler, and settings panels.
function DevTools(props: DevToolsProps): React$Element<any>;
interface DevToolsProps {
bridge: Bridge;
store: Store;
browserName?: 'Chrome' | 'Firefox';
browserTheme?: 'dark' | 'light';
elementsPortalContainer?: HTMLElement;
profilerPortalContainer?: HTMLElement;
settingsPortalContainer?: HTMLElement;
showTabBar?: boolean;
viewElementSource?: Function;
}interface Bridge {
addListener(type: string, callback: Function): void;
removeListener(type: string, callback: Function): void;
send(event: string, payload: any, transferable?: Array<any>): void;
}
interface Wall {
listen: (fn: Function) => void;
send: (event: string, payload: any, transferable?: Array<any>) => void;
}interface Element {
id: number;
parentID: number;
children: Array<number>;
type: ElementType;
displayName: string | null;
key: number | string | null;
ownerID: number;
depth: number;
weight: number;
}
interface InspectedElement {
id: number;
canEditHooks: boolean;
canEditFunctionProps: boolean;
canViewSource: boolean;
context: Object | null;
hooks: Object | null;
props: Object | null;
state: Object | null;
owners: Array<Owner> | null;
source: Object | null;
}
interface Owner {
displayName: string;
id: number;
}interface ReactRenderer {
findHostInstanceByFiber: (fiber: Object) => ?Object;
findFiberByHostInstance: (hostInstance: Object) => ?Object;
version: string;
bundleType: 0 | 1; // PROD | DEV
overrideHookState?: (fiber: Object, id: number, path: Array<string | number>, value: any) => void;
overrideProps?: (fiber: Object, path: Array<string | number>, value: any) => void;
currentDispatcherRef?: { current: null | Object };
}
interface RendererInterface {
cleanup: () => void;
inspectElement: (id: number) => InspectedElement | null;
selectElement: (id: number) => void;
setInContext: (id: number, path: Array<string | number>, value: any) => void;
setInHook: (id: number, index: number, path: Array<string | number>, value: any) => void;
setInProps: (id: number, path: Array<string | number>, value: any) => void;
setInState: (id: number, path: Array<string | number>, value: any) => void;
startProfiling: () => void;
stopProfiling: () => void;
walkTree: () => void;
}const TREE_OPERATION_ADD = 1;
const TREE_OPERATION_REMOVE = 2;
const TREE_OPERATION_RESET_CHILDREN = 3;
const TREE_OPERATION_UPDATE_TREE_BASE_DURATION = 4;
const ElementTypeClass = 1;
const ElementTypeFunction = 2;
const ElementTypeContext = 3;
const ElementTypeForwardRef = 4;
const ElementTypeMemo = 5;
const ElementTypeOtherOrUnknown = 6;
const ElementTypeProfiler = 7;
const ElementTypeRoot = 8;
const ElementTypeSuspense = 9;