1043 Commits
Author SHA1 Message Date
Ruslan LesiutinandGitHub f4354188f5 [DevTools] Buffer Bridge messages during extension reconnects (#37075)
Buffers Bridge messages during extension port reconnects and adds a
readiness handshake for ordered queue flushing. Includes regression
coverage for reconnect delivery and listener cleanup.

Potential scenario could be a long user session, where Chrome kills one
of the extension ports to save resources and then user re-connects by
navigating back to the DevTools UI.
2026-07-23 10:39:16 +01:00
Ruslan LesiutinandGitHub 0cbb5c38d5 [DevTools] Validate Store operation invariants (#37050)
Builds on #37049 by validating Store operation invariants before
mutation. Missing nodes, invalid element types, inconsistent
parent-child relationships, and invalid reorder operations now emit and
throw explicit errors instead of silently continuing with corrupted
state.

Adds a canonical-render regression test for invalid child removal.
2026-07-23 10:39:16 +01:00
Ruslan LesiutinandGitHub 3025d58379 [DevTools] Harden Bridge and Wall lifecycle types (#37049)
Builds on #37048 by replacing `any`-based Bridge and Wall boundaries
with typed `mixed` values and explicit runtime validation. Invalid
messages and post-shutdown operations now throw, while shutdown reliably
flushes queued messages even if cleanup fails.

Strengthens the DevTools Bridge and Wall contracts:
- Models event dictionaries as event-to-payload maps, using `void` for
events without payloads.
- Types `send(event, payload?)` directly, eliminating runtime
payload-arity handling.
- Replaces broad `any` transport types with `mixed` and boundary
validation.
- Throws on invalid lifecycle usage instead of warning or silently
returning.
- Ensures shutdown flushes queued messages even when Wall cleanup fails.
  - Updates Wall implementations and adds Bridge lifecycle coverage.
2026-07-23 10:39:15 +01:00
Ruslan LesiutinandGitHub b2ce8a5aa1 [DevTools] Shut down standalone Bridge on socket close (#37076)
Ensures the standalone DevTools Bridge fully shuts down when its
WebSocket closes, with re-entrancy protection. Adds tests confirming
event-only shutdown leaves the Bridge active while socket closure shuts
it down.
2026-07-23 10:39:15 +01:00
Ruslan LesiutinandGitHub 561425c9c9 [DevTools] Type EventEmitter error handling (#37048)
Tightens `EventEmitter` listener types and fixes error handling so the
first thrown value is preserved while subsequent errors are reported
instead of swallowed. Adds regression coverage for listener failures.
2026-07-23 10:39:15 +01:00
73fe92bf27 [devtools] Document that Store consistency throws must not be worked around (#37035)
Co-authored-by: Hanzo Dev <[email protected]>
2026-07-16 15:04:50 +02:00
Kurtd Daniel BigtasandGitHub b444fd4533 [DevTools] Fix WhatChanged scrolling out of view in profiler sidebar (#36244)
## Summary
Previously, WhatChanged and InspectedElementBadges were rendered inside
the same scrollable [Content
div](https://github.com/facebook/react/blob/main/packages/react-devtools-shared/src/devtools/views/Profiler/SidebarSelectedFiberInfo.js#L130-L147)
as the commit list. When a component had many render timestamps,
scrolling down to pick one caused the "what changed" context to scroll
out of view, requiring the user to scroll back up to see it.

While the profiler already exposes render details via hover tooltips on
fibers, those are transient and pointer-dependent. This change
introduces a sticky header section at the top of the Content div that
holds the current commit summary, badges, and WhatChanged. The section
is collapsible (click or Enter/Space) so users can reclaim vertical
space for the commit list when needed. The commit list now sits below
the sticky header as a sibling, so it scrolls independently while the
summary remains always visible.

Closes #36525 

---
### Current Behavior

https://github.com/user-attachments/assets/05f2889a-4e0d-4464-965a-2821c63c2d30

### Proposed UI
Apologies on this one. I had to [upload it on
YouTube](https://youtu.be/g3KMaqn33G0) since the video is quite big.

---

## How did you test this change?

### Manual Testing
| Case  | Screenshot |
| ------------- | ------------- |
| Expanded simple | <img width="1512" height="948"
alt="expanded-default"
src="https://github.com/user-attachments/assets/00d3bf11-ef98-463e-96ec-c918e58147ff"
/> |
| Collapsed Simple | <img width="1512" height="948"
alt="collapsed-default"
src="https://github.com/user-attachments/assets/75d0ae79-c96f-4c55-9dc1-538ba65037b8"
/> |
| Focused using tab navigation while expanded | <img width="1512"
height="948" alt="expanded-tab-focused-simple"
src="https://github.com/user-attachments/assets/c4098f2d-1c43-4b04-ab69-1e646e79bc8e"
/> |
| Focused using tab navigation while collapsed | <img width="1512"
height="948" alt="collapsed-tab-focused-simple"
src="https://github.com/user-attachments/assets/75840812-b700-44dd-b82d-51d5a867f501"
/> |
| Long render info( expanded ) | <img width="1512" height="948"
alt="long-desc-expanded"
src="https://github.com/user-attachments/assets/53c4d776-55d5-4bb0-818d-a70e032e57d7"
/> |
| Long render info( collapsed ) | <img width="1512" height="948"
alt="long-desc-collapsed"
src="https://github.com/user-attachments/assets/45eb7f6f-fcc6-4f7b-88e7-e3ece9e59d19"
/> |

#### Hover (expanded)

https://github.com/user-attachments/assets/d04668d3-1e0e-44c7-af7e-087d246ec07d

#### Hover (collapsed)

https://github.com/user-attachments/assets/86638b8d-60c2-4cfa-b357-c1104e76767e

---

## Why this matters

This is more than a visual polish issue:

* The Profiler is fundamentally a context-driven debugging tool
* Losing the “why did this render?” explanation during navigation
interrupts analysis flow
* The issue becomes progressively worse on large applications or heavily
interactive surfaces
* Hover tooltips are not an adequate replacement because they are
transient and pointer-dependent
* Keyboard users are disproportionately affected due to repeated
navigation and scrolling

In practice, this increases cognitive load during performance debugging
and makes long profiling sessions unnecessarily cumbersome.
2026-07-07 14:18:51 +01:00
Ruslan LesiutinandGitHub 051ca30658 fix[flow]: apply new type cast syntax (#36938)
Quick follow-up to https://github.com/react/react/pull/36786, which
wasn't rebased onto version of `main` that already had Flow upgraded.
2026-07-06 15:23:25 +01:00
BIKI DASandGitHub aa4358de54 [DevTools] Make component search results directly navigable (#36786)
## Summary

In a large react app, especially when components having similar starting
names like Table, TableColumn, TableCell, TableRow all together 100+
components when rendered in a virtualized table. Traversing the search
result is sometimes difficult with scroll

The component tree search only let you step through matches one at a
time (Enter / Shift+Enter). In large apps with many similarly-named
components (Table, TableRow, TableCell, ...) a search can return 100+
matches in a virtualized list, making a specific match tedious to reach.

- the result counter is an editable, live-scrubbing
  index field: typing a number scrolls to that match as you type
  (clamped to range)

- Fixes re-search getting stuck, clearing the box and retyping the same
  term while a match was still selected snapped back to that same
  component. It now advances to the next match (find-next semantics).
  
  


## How did you test this change?


Adds a SearchableTable example to the DevTools shell and unit tests for
the new action and the retype behavior.




https://github.com/user-attachments/assets/7ea9801a-7bcb-4e8f-bf73-a5307a0fdbae


cc @hoxyq Let me know what do you feel about this feature, if its
helpful for devtools.
2026-07-06 14:59:51 +01:00
Anas KhanandGitHub e01aa00cd7 [DevTools] Fix printOperationsArray decode of applied activity slice change (#36935)
`printOperationsArray` in `react-devtools-shared` walks an operations
array under
the invariant that each `switch` case leaves `i` pointing at the next
opcode (loop
header at `packages/react-devtools-shared/src/utils.js`).

The `TREE_OPERATION_APPLIED_ACTIVITY_SLICE_CHANGE` case broke that
invariant:

```js
case TREE_OPERATION_APPLIED_ACTIVITY_SLICE_CHANGE: {
  i++;                                        // skip opcode -> i now at the value slot
  const activitySliceIDChange = operations[i + 1]; // reads the slot AFTER the value; i not advanced
  ...
}
```

The operation is exactly two slots, `[opcode, activitySliceID]` (see the
writer in
`packages/react-devtools-shared/src/backend/fiber/renderer.js`, which
pushes the
opcode then the id). So the case did two things wrong:

1. It logged the wrong number: `operations[i + 1]` reads the slot
*after* the
value (the next operation's opcode, or `undefined` at the end of the
array).
2. It left `i` pointing at the value slot, so the outer `while (i <
operations.length)` loop re-read the activity-slice id as an opcode. For
any
non-zero slice id that falls through to `default: throw
Error("Unsupported
   Bridge operation ...")`, aborting the whole dump.

The two canonical decoders of this same operation both use the correct
pattern
(skip the opcode, then read *and* advance past the value):

- `devtools/store.js`: `i++; nextActivitySliceID = operations[i++];`
- `devtools/views/Profiler/CommitTreeBuilder.js`: `i++; const
activitySliceIDChange = operations[i++];`

This change makes `printOperationsArray` match them by reading
`operations[i++]`.

This is a debug-only diagnostic path: the only caller is the
`__DEBUG__`-guarded
dump in `backend/legacy/renderer.js`, so it is not a production crash.
The bug was
introduced in #34908.

## How did you test this change?

Added a regression test for `printOperationsArray` in
`packages/react-devtools-shared/src/__tests__/utils-test.js`. The
fixture chains
two activity-slice operations, `[rendererID, rootID, stringTableSize=0,
opcode, 42,
opcode, 0]`; the trailing operation is what forces the reader to advance
past the
first value slot rather than re-read it. It asserts the call does not
throw, logs
once, and that the message contains both `Applied activity slice change
to 42` and
`Reset applied activity slice`.

Ran the DevTools Jest project (built first, as that project requires a
build):

- With the fix: 51/51 pass, including the new test.
- Reverting only the one-line fix back to `operations[i + 1]` and
rebuilding: the
new test fails with `Unsupported Bridge operation "42"` (exactly the
predicted
  failure), 50 pass / 1 fail. Restored the fix and it is green again.

`yarn prettier-check` and `yarn linc` are clean on the changed files.
2026-07-06 14:27:17 +01:00
Anas KhanandGitHub 3deee48461 [react-devtools] Keep console specifiers literal when no argument is supplied (#36930)
`formatConsoleArguments` in

`packages/react-devtools-shared/src/backend/utils/formatConsoleArguments.js`
is used by the DevTools backend (via `hook.js`) to inline `console.*`
printf-style substitutions after stripping React's appended component
stack.
For `%s`/`%d`/`%i`/`%f` it consumes the next argument with
`args.splice(argumentsPointer, 1)` and formats the result.

When a format string has more specifiers than arguments, `splice`
returns an
empty array, so `arg` is `undefined` and the specifier is rendered as
text:
`%s` becomes `"undefined"`, and `%d`/`%i`/`%f` become `"NaN"`.

```js
formatConsoleArguments('%s %s', 'the');
// before: ['the undefined']
// after:  ['the %s']
```

Browsers and Node's `util.format` leave an unmatched specifier as a
literal
(`console.log('%s %s', 'a')` prints `a %s`; `console.log('%d')` prints
`%d`).
So a message like `console.warn('value: %s')` was shown in DevTools as
`value: undefined` instead of `value: %s`.

This guards each of the `%d`/`%i`, `%f`, and `%s` cases on argument
availability (`argumentsPointer >= args.length`): when nothing is left
to
consume it keeps the specifier text and does not splice, mirroring the
existing trailing-`%` handling added in #36852. An explicitly passed
`undefined`/`null` argument is unchanged and still renders as
`undefined`/`null`, since a value is present at that position (the
`formats nullish values` test still passes).

## How did you test this change?

Added a regression test to the existing `formatConsoleArguments`
describe
block in `packages/react-devtools-shared/src/__tests__/utils-test.js`:

```js
it('keeps specifiers literal when no argument is supplied', () => {
  expect(formatConsoleArguments('%s %s', 'the')).toEqual(['the %s']);
  expect(formatConsoleArguments('%s %d', 'value')).toEqual(['value %d']);
  expect(formatConsoleArguments('%s %i', 'value')).toEqual(['value %i']);
  expect(formatConsoleArguments('%s %f', 'value')).toEqual(['value %f']);
});
```

Each assertion fails on `main` (it produces `['the undefined']` and
`['value NaN']`) and passes with the fix.

Commands run locally:

```
yarn test --build --project devtools packages/react-devtools-shared/src/__tests__/utils-test.js
# 51 passed, 51 total

yarn lint packages/react-devtools-shared/src/backend/utils/formatConsoleArguments.js \
          packages/react-devtools-shared/src/__tests__/utils-test.js
# Lint passed.

yarn flow dom-node
# No errors!

yarn prettier-check packages/react-devtools-shared/src/backend/utils/formatConsoleArguments.js \
                    packages/react-devtools-shared/src/__tests__/utils-test.js
# clean
```

Cross-checked the expected output against Node `util.format`:
`util.format('%s %s', 'the')` -> `the %s`; `util.format('%d')` -> `%d`.
2026-07-06 14:18:10 +01:00
Anas KhanandGitHub 45a8cf5349 [react-devtools] substitute %i and %f in console format strings (#36929)
`formatConsoleArgumentsToSingleString` in
`packages/react-devtools-shared/src/backend/utils/index.js`
inlines `console.*` printf-style substitutions into a single string.
That string is
used both as the dedup key and as the displayed text for per-component
warnings/errors.

The `switch` that consumes the captured flag handles `s`, `d`, `i`, and
`f`, and the
function's own header comment says it "Implements s, d, i and f
placeholders". But
the substitution regex only captured `[jds]`:

```js
const REGEXP = /(%?)(%([jds]))/g;
```

So `%i` and `%f` were never matched. The `case 'i'` and `case 'f'` arms
were dead
code: the specifier was emitted literally and its argument was never
consumed. Worse,
because the unmatched specifier does not shift its argument, every
following specifier
in the same format string then binds to the wrong argument (a cascading
off-by-one
over the remaining args).

`%i` and `%f` are standard console integer/float specifiers (Node
`util.format` and
browsers both support them), so this affected common log formats. The
fix adds `i`
and `f` to the regex class so the existing switch arms run:

```js
const REGEXP = /(%?)(%([jdisf]))/g;
```

This is a one-character-class change that reconciles the regex with the
switch and
the header comment. The pre-existing behavior that `%j` is matched but
has no
`case` (so it falls through unchanged) is intentionally left as-is; it
is out of
scope for this fix.

## How did you test this change?

Added three regression tests to the existing
`formatConsoleArgumentsToSingleString`
describe block in
`packages/react-devtools-shared/src/__tests__/utils-test.js`:

- `formatConsoleArgumentsToSingleString('%i', 3.14)` -> `'3'`
- `formatConsoleArgumentsToSingleString('%f', 3.5)` -> `'3.5'`
- `formatConsoleArgumentsToSingleString('a %i b %s', 7, 'x')` -> `'a 7 b
x'` (locks
  in argument alignment)

Commands run locally (experimental devtools bundles):

```
yarn build-for-devtools
yarn test --build --project=devtools -r=experimental packages/react-devtools-shared/src/__tests__/utils-test.js
```

Result: `Tests: 53 passed, 53 total`.

To confirm the tests actually cover the bug, I reverted the
one-character fix back to
`[jds]` and reran: the three new tests fail exactly as the bug predicts,
e.g. `%i`
yields `"%i 3.14"` and `a %i b %s` yields `"a %i b 7 x"` (the `%s` binds
to `7`
instead of `x`, showing the off-by-one). Restoring the fix makes them
pass again.

Also green:

```
yarn linc            # ESLint on changed files: passed
yarn prettier        # no files reflagged
yarn flow dom-node   # No errors!
```
2026-07-06 14:16:15 +01:00
Jiwon ChoiandGitHub 4950b5b789 [DevTools] Add ignore-listed stack frame disclosure (#36828) 2026-06-25 15:57:28 +02:00
uditandGitHub bb3548eff8 [react-devtools] Fix trailing percent sign in formatConsoleArguments (#36852)
## Summary

`formatConsoleArguments` (used by the DevTools backend to inline console
substitutions) walks the format string and inlines `%s`/`%d`/`%i`/`%f`
arguments while leaving `%c`/`%o`/`%O` in place. For each `%` it reads
the **next** character to decide what to do.

When the format string ends with a lone `%` — e.g.
`console.log('Progress 100%', value)` — the character after `%` is
`undefined`, and the `default` branch ran:

```js
template += `%${nextChar}`; // -> "%undefined"
```

So the function emitted the literal text `%undefined`:

```js
formatConsoleArguments('Progress 100%', 'extra');
// before: ['Progress 100%undefined', 'extra']
// after:  ['Progress 100%', 'extra']
```

Browsers render a trailing `%` in a console format string as a literal
percent sign, so this PR keeps it as `%` when there is no following
character.

## How did you test this change?

Added a `keeps a trailing percent sign` test to the existing
`formatConsoleArguments` suite in `utils-test.js` (covering both a bare
trailing `%` and one that follows another substitution). Since the
function is pure and import-free, I also verified the patched logic
against every existing case in that suite plus the new ones to confirm
there are no regressions.
2026-06-22 21:29:09 +01:00
Ruslan LesiutinandGitHub 15794a4907 [DevTools] Extract getDispatcherRef and fork inspectHooks() to avoid pulling React into the build artifact (#36681)
This is just a refactor to unblock the work on the Facade. This PR
contains 2 changes:
1. `getDispatcherRef` was extracted from `fiber/renderer` into its own
shared module.
2. `inspectHooksOfFiber` was forked. The new version specifies
dispatcher as required and doesn't set it to default as
`ReactSharedInternals`, which are pulling the whole `react` module as
part of them:


https://github.com/facebook/react/blob/3f6785f59e8871e232e94276db82b53299a36bb3/packages/shared/ReactSharedInternals.js#L10-L15
2026-06-15 08:27:11 +01:00
Sam ZhouandGitHub fa0542dc66 [flow] Update files synced to react-native to use modern flow syntax (#36759)
## Summary

This diff bumps a few hermes-parser related dependencies so that it can
consume modern Flow syntax. In addition, it makes targeted changes to
two files that will be synced to react-native so that we can enforce we
only use modern flow syntax in react-native.

## How did you test this change?

flow
2026-06-12 12:56:15 -04:00
Sam ZhouandGitHub 4cc753e62f [flow] Bump flow to v0.317.0 (#36701)
## Summary

Mostly changing the casting syntax from `(x: Y)` to `x as Y`, as the old
syntax was deprecated and always causing an error in Flow

## How did you test this change?

`yarn flow-ci`
2026-06-05 19:17:18 -04:00
Sam ZhouandGitHub 78cf315a48 [flow] Bump flow to v0.307.1 (#36199)
## Summary

Notable changes:
- Only $FlowFixMe, $FlowExpectedError suppression comments are supported
- All suppression comments need error code
- A lot of new invalid-compare and constant-condition errors suppressed.
These errors reveal potential logical bugs

## How did you test this change?

`yarn flow-ci`
2026-06-05 13:54:38 -04:00
Ruslan LesiutinandGitHub e72dc6f733 fix[describeClassComponentFrame]: invoke constructor with new keyword (#36455)
For JavaScript runtimes that do not have
[`Reflect`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Reflect)
supported, we had a fall back that was calling the constructor with
overridden `this` context via
```
fn.apply(Fake.prototype);
```

In ES6, it is required to call constructor only with the `new` keyword,
otherwise the runtime is expected to throw a corresponding TypeError:
```
TypeError: Class constructor <> cannot be invoked without 'new'
```

We've observed this error in Hermes runtime, but this is applicable to
V8 or any other runtime. The only reason why V8 wasn't affected is
because it implemented Reflect APIs.

Instead of the incorrect call, we will fall back to calling `new fn()`,
but with a temporary patched prototype of the class, which would make a
trap out of the setter for `props` object. We use the same approach when
`Reflect` APIs are available, but instead of modifying the prototype, we
pass the fake context:

https://github.com/facebook/react/blob/878b6fb60ce5a2347ec1bfa0741edf0b2d08f157/packages/shared/ReactComponentStackFrame.js#L129-L148


---

See tests implemented. Without the changes, the test would fail with the
`TypeError` mentioned above.
2026-05-13 22:05:14 +01:00
zxuhan7andGitHub 087d057db4 [DevTools] Preserve -Infinity in inspected values (#36347)
## Summary

`getDataType` collapsed both `Infinity` and `-Infinity` to the
`'infinity'` data type, so a `-Infinity` value coming from inspected
props/state/hooks was rehydrated on the frontend as `Infinity`.

This adds a `'-infinity'` `DataType`, routes it through
`dehydrate`/`hydrate` alongside the existing `'infinity'` arm, and makes
`smartParse`/`smartStringify` (used for editable hook values) symmetric.

## Files

- `packages/react-devtools-shared/src/utils.js` — extend `DataType`,
split sign in `getDataType`, route `'-infinity'` through
`formatDataForPreview`.
- `packages/react-devtools-shared/src/hydration.js` — `dehydrate` and
`hydrate` cases for `'-infinity'`.
- `packages/react-devtools-shared/src/devtools/utils.js` — `smartParse`
accepts `'-Infinity'`; `smartStringify` returns `'-Infinity'` for
negative infinite values.
-
`packages/react-devtools-shared/src/__tests__/inspectedElement-test.js`
and `legacy/inspectElement-test.js` — added `minus_infinity={-Infinity}`
to the simple-data-types tests + snapshots.
-
`packages/react-devtools-shell/src/app/InspectableElements/SimpleValues.js`
— added `minusInfinity` to the dev shell so the path is exercised
manually.

## Test plan

- [x] `yarn prettier` / `yarn linc`
- [x] `yarn flow dom-node` — no errors
- [x] `yarn test --silent --no-watchman -t "should support simple data
types"` (source channel)
- [x] `yarn test-www --silent --no-watchman -t "should support simple
data types"` (www-modern)
- [ ] `yarn test-build-devtools` — relies on a built bundle; left to CI
per repo policy.

Fixes #32552
2026-05-02 09:50:40 +01:00
petertdinhandGitHub 72e06b7129 fix(devtools): clear highlight when mouse leaves DevTools panel (#36177)
Fixes #17855

When hovering a component in the DevTools Components inspector, a
highlight overlay appears on the inspected page. The highlight is
cleared via `onMouseLeave` on the tree container `div`. But this React
synthetic event only fires when the pointer transitions between elements
_within the same document_. When the user moves their mouse out of the
DevTools panel window entirely (e.g. to the browser viewport), no
element in the React tree receives `mouseleave`, so
`clearHostInstanceHighlight` is never sent over the bridge and the
overlay persists on the page.

The fix adds a native `mouseleave` listener on the DevTools panel's
`ownerDocument` in `Tree.js`. When the pointer exits the panel viewport,
it fires `clearHighlightHostInstance` and removes the overlay. Using
`ownerDocument` (rather than document) is consistent with the existing
pattern in `Tree.js` for browser extension compatibility.

How did you test this change?

Tested manually using the Chrome extension:

1. Opened React DevTools → Components tab on a React app
2. Hovered a component in the tree — highlight appeared on the page ✓
3. Moved the mouse out of the DevTools panel into the browser viewport —
highlight cleared immediately ✓ (previously it persisted)
4. Moved the mouse back into the panel and hovered a component —
highlighting still works normally ✓
5. Unhovered within the panel — highlight still clears correctly ✓

Ran the DevTools test suite: yarn test --no-watchman ReactDevTools — all
tests pass.
2026-05-02 09:50:26 +01:00
Sebastian "Sebbie" SilbermannandGitHub d3449f6200 [DevTools] Remove spurious warnings when creating Suspense timeline from a Transition (#36133) 2026-04-08 18:49:35 +02:00
Bodhi Russell SilberlingandGitHub 2cf6d3e757 Fix typos: occured->occurred, teh->the, accomodate->accommodate (#35616)
Fixed spelling errors in comments and error messages:
- Fixed 'occured' -> 'occurred' in ReactAsyncActions-test.js
- Fixed 'teh' -> 'the' in ReactFiberConfigDOM.js
- Fixed 'occured' -> 'occurred' in ErrorBoundary.js
- Fixed 'accomodate' -> 'accommodate' in InferMutationAliasingEffects.ts

<!--
  Thanks for submitting a pull request!
We appreciate you spending the time to work on these changes. Please
provide enough information so that others can review your pull request.
The three fields below are mandatory.

Before submitting a pull request, please make sure the following is
done:

1. Fork [the repository](https://github.com/facebook/react) and create
your branch from `main`.
  2. Run `yarn` in the repository root.
3. If you've fixed a bug or added code that should be tested, add tests!
4. Ensure the test suite passes (`yarn test`). Tip: `yarn test --watch
TestName` is helpful in development.
5. Run `yarn test --prod` to test in the production environment. It
supports the same options as `yarn test`.
6. If you need a debugger, run `yarn test --debug --watch TestName`,
open `chrome://inspect`, and press "Inspect".
7. Format your code with
[prettier](https://github.com/prettier/prettier) (`yarn prettier`).
8. Make sure your code lints (`yarn lint`). Tip: `yarn linc` to only
check changed files.
  9. Run the [Flow](https://flowtype.org/) type checks (`yarn flow`).
  10. If you haven't already, complete the CLA.

Learn more about contributing:
https://reactjs.org/docs/how-to-contribute.html
-->

## Summary

<!--
Explain the **motivation** for making this change. What existing problem
does the pull request solve?
-->

## How did you test this change?

<!--
Demonstrate the code is solid. Example: The exact commands you ran and
their output, screenshots / videos if the pull request changes the user
interface.
How exactly did you verify that your PR solves the issue you wanted to
solve?
  If you leave this empty, your PR will very likely be closed.
-->
2026-03-31 16:01:58 -07:00
Ruslan LesiutinandGitHub e70ba593ac [DevTools] feat: display subtree for Activity and dim in hidden mode (#36094)
With this change, Components panel will display subtree of the Activity.
When it is in hidden mode, the subtree will be dimmed.

Added Jest tests and a sandbox case to `react-devtools-shell`.

Demo:


https://github.com/user-attachments/assets/69a2e8d6-585d-4fcd-b57e-e9ae06d0a1b3
2026-03-23 14:29:38 +00:00
Ruslan LesiutinandGitHub 6844e8011f [DevTools] chore: extract getInternalReactConstants (#35986)
The test is quite naive, but worth having as additional check for
changes in this logic.
2026-03-20 14:17:15 +00:00
Ruslan LesiutinandGitHub aba4f90381 [DevTools] fix: don't show empty suspended by section (#36011)
The potential paddings and margins for the empty block are already
handled via CSS selectors.
2026-03-12 14:19:21 -07:00
Sebastian "Sebbie" SilbermannandGitHub 0caec283d6 [DevTools] Ignore new production renderers if we already use "worse" versions of React on a page (#35994) 2026-03-11 10:26:35 +01:00
Sebastian "Sebbie" SilbermannandGitHub b4d0874823 [DevTools] Fix a crash when rendering a new class Component when simulating errored state (#35985) 2026-03-10 18:05:48 +01:00
932543873d [DevTools] Fix null ref crash in ContextMenu when items list is empty (#35929)
Co-authored-by: Sebastian Sebbie Silbermann <[email protected]>
2026-03-05 16:52:23 +01:00
Ruslan LesiutinandGitHub 8aa3683da6 [DevTools] chore: extract pure functions from fiber/renderer.js (#35924)
I am in a process of splitting down the renderer implementation into
smaller units of logic that can be reused. This change is about
extracting pure functions only.
2026-03-03 12:27:05 +00:00
Sebastian "Sebbie" SilbermannandGitHub ad10f7c20d [DevTools] Only schedule a single update per Supense when changing timeline (#35927) 2026-03-03 12:21:46 +01:00
Sebastian "Sebbie" SilbermannandGitHub 8d395dce04 [DevTools] Fix alignment of breadcrumbs separator (#35817) 2026-02-18 10:53:49 -08:00
Ruslan LesiutinandGitHub a1077c4596 [DevTools] Check suspense child node presence in parentSuspense.children before removing (#35775)
Currently, this silently removes the last child in the list, which
doesn't contain the `id`.
2026-02-13 15:33:22 +00:00
Sebastian "Sebbie" SilbermannandGitHub 9e1fb1aab8 [DevTools] Dedicated empty state for roots that aren't suspended by anything (#35769) 2026-02-12 17:48:02 +01:00
Sebastian "Sebbie" SilbermannandGitHub f60a406297 [DevTools] Enable Suspense tab by default (#35768) 2026-02-12 16:50:29 +01:00
Ruslan LesiutinandGitHub a3c56f2a29 [DevTools] Remove experimental __IS_INTERNAL_MCP_BUILD__ flag and related code (#35755)
This is unused.
2026-02-11 16:59:43 +00:00
Sebastian "Sebbie" SilbermannandGitHub 6e94f65dfa [DevTools] Display React.optimisticKey in key positions (#35760) 2026-02-11 00:35:36 +01:00
Sebastian "Sebbie" SilbermannandGitHub 2bb178edeb [DevTools] Only block child Suspense boundaries if the parent has all shared suspenders removed (#35737) 2026-02-10 17:52:35 +01:00
d0206107b3 Consistent handling of work tags for rename, delete, and overriding state (#35740)
Co-authored-by: Ruslan Lesiutin <[email protected]>
2026-02-10 16:35:02 +01:00
Sebastian "Sebbie" SilbermannandGitHub 75593946c9 [DevTools] Fix memory leak when unmounting hoistables (#35741) 2026-02-10 13:09:23 +01:00
Sebastian "Sebbie" SilbermannandGitHub c6983ae9a2 [test] Include uniqueSuspenders in Suspense tree snapshots (#35736) 2026-02-10 12:51:58 +01:00
Ruslan LesiutinandGitHub 4210e49bb3 [DevTools] Don't capture durations for disconnected subtrees when profiling (#35718)
After https://github.com/facebook/react/pull/34089, when updating
(possibly, mounting) inside disconnected subtree, we don't record this
as an operation. This only happens during reconnect. The issue is that
`recordProfilingDurations()` can be called, which diffs tree base
duration and reports it to the Frontend:

https://github.com/facebook/react/blob/cba2bc83d8ce7887fabb90775d25b67ade36e5c3/packages/react-devtools-shared/src/backend/fiber/renderer.js#L4506-L4521

This operation can be recorded before the "Add" operation, and it will
not be resolved properly on the Frontend side.

Before the fix:
```
commit tree › Suspense › should handle transitioning from fallback back to content during profiling

    Could not clone the node: commit tree does not contain fiber "5". This is a bug in React DevTools.

      162 |     const existingNode = nodes.get(id);
      163 |     if (existingNode == null) {
    > 164 |       throw new Error(
          |             ^
      165 |         `Could not clone the node: commit tree does not contain fiber "${id}". This is a bug in React DevTools.`,
      166 |       );
      167 |     }

      at getClonedNode (packages/react-devtools-shared/src/devtools/views/Profiler/CommitTreeBuilder.js:164:13)
      at updateTree (packages/react-devtools-shared/src/devtools/views/Profiler/CommitTreeBuilder.js:348:24)
      at getCommitTree (packages/react-devtools-shared/src/devtools/views/Profiler/CommitTreeBuilder.js:112:20)
      at ProfilingCache.getCommitTree (packages/react-devtools-shared/src/devtools/ProfilingCache.js:40:46)
      at Object.<anonymous> (packages/react-devtools-shared/src/__tests__/profilingCommitTreeBuilder-test.js:257:44)
```
2026-02-09 22:18:17 +00:00
Sebastian "Sebbie" SilbermannandGitHub c31165650f [DevTools] Allow renaming Host Component props (#35735) 2026-02-09 23:14:46 +01:00
Sebastian "Sebbie" SilbermannandGitHub e33fa11c51 [DevTools] Fix crash when revealing stable, filtered <Activity> children (#35734) 2026-02-09 22:52:24 +01:00
Ruslan LesiutinandGitHub 4d6a84745e [DevTools] Fix false-positive re-render reports for filtered nodes (#35723)
Fixes https://github.com/facebook/react/issues/33423,
https://github.com/facebook/react/issues/35245,
https://github.com/facebook/react/issues/19732.

As demoed
[here](https://github.com/facebook/react/issues/33423#issuecomment-2970750588),
React DevTools incorrectly highlights re-renders for descendants of
filtered-out nodes that didn't actually render.

There were multiple fixes suggesting changes in `didFiberRender()`
function, but these doesn't seem right, because this function is used in
a context of whether the Fiber actually rendered something (updated),
not re-rendered compared to the previous Fiber.

Instead, this PR adds additional validation at callsites that either
used for highlighting re-renders or capturing tree base durations and
are relying on `didFiberRender`. I've also added a few tests that
reproduce the failure scenario. Without the changes, the tests are
failing.
2026-02-09 20:39:33 +00:00
Sebastian "Sebbie" SilbermannandGitHub 048e4a3e1a [Fiber] Avoid duplicate debug info for array children (#35733) 2026-02-09 20:36:56 +01:00
Sebastian "Sebbie" SilbermannandGitHub cba2bc83d8 [test] Move profilingCommitTreeBuilder to versioned renderer (#35711) 2026-02-06 16:00:43 +01:00
Sebastian "Sebbie" SilbermannandGitHub f0425d4279 [DevTools] Fix broken commit tree builder for initial operations (#35710) 2026-02-06 15:16:23 +01:00
Sebastian "Sebbie" SilbermannandGitHub 514d8140c3 [DevTools] Separate breadcrumbs with » (#35705) 2026-02-06 00:40:31 +01:00
Sebastian "Sebbie" SilbermannandGitHub 50a8fab165 [DevTools] Avoid scrollbars in Suspense breadcrumbs (#35700) 2026-02-05 23:27:57 +01:00