DevTools Integration Hook
The reconciler exposes Fiber activity through optional callbacks on __REACT_DEVTOOLS_GLOBAL_HOOK__. Its adapter reports root scheduling, commits, post-commit work, unmounts, Strict Mode state, and scheduling-profiler events.
This boundary keeps DevTools integration optional and defensive: the global hook may be absent or disabled, and instrumentation errors are caught instead of escaping renderer setup or commit reporting.
Sources: packages/react-reconciler/src/ReactFiberDevToolsHook.js:93-109, packages/react-reconciler/src/ReactFiberDevToolsHook.js:111-152, packages/react-reconciler/src/ReactFiberDevToolsHook.js:154-170, packages/react-reconciler/src/ReactFiberDevToolsHook.js:172-185, packages/react-reconciler/src/ReactFiberDevToolsHook.js:187-207, packages/react-reconciler/src/ReactFiberDevToolsHook.js:217-226, packages/react-reconciler/src/ReactFiberDevToolsHook.js:486-495, packages/react-reconciler/src/ReactFiberDevToolsHook.js:50-83, packages/react-reconciler/src/ReactFiberDevToolsHook.js:111-150
Core concepts
The global hook
The global hook is the __REACT_DEVTOOLS_GLOBAL_HOOK__ object that the reconciler reads when injectInternals runs. The shared DevTools code installs this hook on window, while the facade can install a minimal hook or attach to an existing one such as the browser extension's hook.
Sources: packages/react-reconciler/src/ReactFiberDevToolsHook.js:50-55, packages/react-devtools-shared/src/hook.js:1-8, packages/react-devtools-facade/src/DevToolsFacade.js:202-214
Renderer injection
Renderer injection passes reconciler internals to hook.inject, stores its returned value in rendererID, and stores the hook reference in injectedHook.
Sources: packages/react-reconciler/src/ReactFiberDevToolsHook.js:73-78
Lifecycle notifications
Lifecycle adapters forward root scheduling, commits, post-commit work, unmounts, and Strict Mode state to optional methods on injectedHook.
| Adapter | Hook method | What it reports |
|---|---|---|
onScheduleRoot | onScheduleFiberRoot | A scheduled Fiber root and its children. |
onCommitRoot | onCommitFiberRoot | A commit, scheduler priority when enabled, and captured-error state. |
onPostCommitRoot | onPostCommitFiberRoot | Work after a commit. |
onCommitUnmount | onCommitFiberUnmount | A Fiber being unmounted. |
setIsStrictModeForDevtools | setStrictMode | The current Strict Mode value. |
Sources: packages/react-reconciler/src/ReactFiberDevToolsHook.js:93-109, packages/react-reconciler/src/ReactFiberDevToolsHook.js:111-152, packages/react-reconciler/src/ReactFiberDevToolsHook.js:154-170, packages/react-reconciler/src/ReactFiberDevToolsHook.js:172-185, packages/react-reconciler/src/ReactFiberDevToolsHook.js:187-207, packages/react-reconciler/src/ReactFiberDevToolsHook.js:93-107, packages/react-reconciler/src/ReactFiberDevToolsHook.js:111-142, packages/react-reconciler/src/ReactFiberDevToolsHook.js:154-168, packages/react-reconciler/src/ReactFiberDevToolsHook.js:172-183, packages/react-reconciler/src/ReactFiberDevToolsHook.js:187-205
Profiling hooks
Profiling callbacks are a separate callback set stored in injectedProfilingHooks. injectProfilingHooks replaces that set, and profiling adapters call it only when enableSchedulingProfiler is enabled and the requested callback exists.
Sources: packages/react-reconciler/src/ReactFiberDevToolsHook.js:44, packages/react-reconciler/src/ReactFiberDevToolsHook.js:211-215, packages/react-reconciler/src/ReactFiberDevToolsHook.js:217-225
How the reconciler injects into the hook
injectInternals returns false when the global hook is absent. A disabled hook returns true without registering, and a hook without Fiber support is treated as present while development builds log that the installed DevTools version is too old.
When Fiber support is available, the function calls hook.inject(internals), saves the returned renderer identifier, and stores the hook reference. If injection throws, the error is caught and logged in development.
export function injectInternals(internals: Object): boolean {
if (typeof __REACT_DEVTOOLS_GLOBAL_HOOK__ === 'undefined') {
return false;
}
const hook = __REACT_DEVTOOLS_GLOBAL_HOOK__;
if (hook.isDisabled) {
return true;
}
try {
rendererID = hook.inject(internals);
injectedHook = hook;
} catch (err) {The important boundary is that the reconciler reads an externally installed hook and calls its injection method; the shared hook implementation explicitly avoids a direct dependency on the DevTools backend.
The facade may install before React initializes so the first commit is captured, or attach to an existing hook rather than replacing it.
Evidence
- reconciler-adapterpackages/react-reconciler/src/ReactFiberDevToolsHook.js:50
- global-hookpackages/react-reconciler/src/ReactFiberDevToolsHook.js:50
- renderer-registrationpackages/react-reconciler/src/ReactFiberDevToolsHook.js:73
- initialization-errorpackages/react-reconciler/src/ReactFiberDevToolsHook.js:78
Sources: packages/react-reconciler/src/ReactFiberDevToolsHook.js:50-71, packages/react-reconciler/src/ReactFiberDevToolsHook.js:73-83, packages/react-reconciler/src/ReactFiberDevToolsHook.js:50-78, packages/react-devtools-shared/src/hook.js:1-8, packages/react-devtools-facade/src/DevToolsFacade.js:202-214
How mounts, updates, and commits are reported
Root scheduling is reported by onScheduleRoot only in development, when injectedHook exposes onScheduleFiberRoot. The callback receives rendererID, the root, and the root children.
A commit is reported by onCommitRoot through onCommitFiberRoot. The adapter derives didError from the current root flags. When enableProfilerTimer is enabled, it also maps event priority to a scheduler priority before calling the hook.
Afterward, onPostCommitRoot calls onPostCommitFiberRoot when available. Unmount reporting uses onCommitUnmount, which passes rendererID and the Fiber to onCommitFiberUnmount.
Each lifecycle adapter checks for the relevant hook method before calling it. Calls are wrapped in try/catch, and development builds log only the first caught instrumentation error because hasLoggedError is set after that error.
Sources: packages/react-reconciler/src/ReactFiberDevToolsHook.js:93-107, packages/react-reconciler/src/ReactFiberDevToolsHook.js:111-142, packages/react-reconciler/src/ReactFiberDevToolsHook.js:154-168, packages/react-reconciler/src/ReactFiberDevToolsHook.js:172-183, packages/react-reconciler/src/ReactFiberDevToolsHook.js:111-150
How profiling data is forwarded
injectProfilingHooks stores a DevToolsProfilingHooks object in injectedProfilingHooks.
The profiling surface includes commit, render, layout-effect, and passive-effect boundaries. It also reports component render boundaries, errors, suspensions, scheduled renders, force updates, and state updates.
For example, markCommitStarted checks enableSchedulingProfiler, verifies that injectedProfilingHooks is non-null, verifies that markCommitStarted is callable, and then forwards the lanes. The same guard structure appears in the render and state-update adapters.
Sources: packages/react-reconciler/src/ReactFiberDevToolsHook.js:211-215, packages/react-reconciler/src/ReactFiberDevToolsHook.js:217-226, packages/react-reconciler/src/ReactFiberDevToolsHook.js:239-248, packages/react-reconciler/src/ReactFiberDevToolsHook.js:357-370, packages/react-reconciler/src/ReactFiberDevToolsHook.js:372-385, packages/react-reconciler/src/ReactFiberDevToolsHook.js:387-396, packages/react-reconciler/src/ReactFiberDevToolsHook.js:409-418, packages/react-reconciler/src/ReactFiberDevToolsHook.js:431-440, packages/react-reconciler/src/ReactFiberDevToolsHook.js:464-473, packages/react-reconciler/src/ReactFiberDevToolsHook.js:475-484, packages/react-reconciler/src/ReactFiberDevToolsHook.js:486-495, packages/react-reconciler/src/ReactFiberDevToolsHook.js:217-225
Why the integration is often a no-op
When the global hook is absent, injectInternals returns early before reaching the assignment that stores injectedHook. Consequently, the lifecycle adapters have no injected hook reference to use.
Root scheduling is explicitly development-gated, and profiling is explicitly enableSchedulingProfiler-gated. In a build without the global hook and without scheduling profiling, these paths therefore perform no DevTools callback work.
Sources: packages/react-reconciler/src/ReactFiberDevToolsHook.js:50-55, packages/react-reconciler/src/ReactFiberDevToolsHook.js:73-78, packages/react-reconciler/src/ReactFiberDevToolsHook.js:111-113, packages/react-reconciler/src/ReactFiberDevToolsHook.js:93-109, packages/react-reconciler/src/ReactFiberDevToolsHook.js:217-225
How it connects
The shared hook and facade are part of React DevTools Extension, which explains the page-level hook's installation and the extension-side consumer.
The Fiber roots and Fibers passed to these callbacks are described in Fiber Architecture, while commit timing aligns with Commit Phase. The scheduler priority and lane values passed to DevTools connect to Lanes & Priority and Scheduler Priorities & Task Queue.
Sources: packages/react-devtools-shared/src/hook.js:1-8, packages/react-devtools-facade/src/DevToolsFacade.js:202-214, packages/react-reconciler/src/ReactFiberDevToolsHook.js:111-142, packages/react-reconciler/src/ReactFiberDevToolsHook.js:217-225
Key takeaways
injectInternalsreads the global hook, calls hook.inject, and storesrendererIDandinjectedHook.- Lifecycle adapters report root scheduling, commits, post-commit work, unmounts, and Strict Mode state.
- The shared DevTools code installs the page-level hook, and the facade can install or attach to one.
- Profiling callbacks are separately injected and gated by
enableSchedulingProfiler. - Missing hooks and caught instrumentation errors keep DevTools reporting from interrupting reconciler work.
Sources: packages/react-reconciler/src/ReactFiberDevToolsHook.js:50-78, packages/react-reconciler/src/ReactFiberDevToolsHook.js:93-109, packages/react-reconciler/src/ReactFiberDevToolsHook.js:111-152, packages/react-reconciler/src/ReactFiberDevToolsHook.js:154-170, packages/react-reconciler/src/ReactFiberDevToolsHook.js:172-185, packages/react-reconciler/src/ReactFiberDevToolsHook.js:187-207, packages/react-devtools-shared/src/hook.js:1-8, packages/react-devtools-facade/src/DevToolsFacade.js:202-214, packages/react-reconciler/src/ReactFiberDevToolsHook.js:211-215, packages/react-reconciler/src/ReactFiberDevToolsHook.js:217-226, packages/react-reconciler/src/ReactFiberDevToolsHook.js:486-495, packages/react-reconciler/src/ReactFiberDevToolsHook.js:50-83