@samitouri / QOS-React-1 / commits / 705055d7ac

[DevTools] Enable Suspense tab by default (#35768)

Sebastian "Sebbie" Silbermann committed Feb 12, 2026 at 16:50 UTC 705055d7ac3da03927758b22d8aea4b2e5913961
5 files changed +12 -59
packages/react-devtools-extensions/src/main/index.js
+1 -6
@@ -167,10 +167,6 @@ function createBridgeAndStore() {
167 supportsClickToInspect: true,
168 });
169
170 - store.addListener('enableSuspenseTab', () => {
171 - createSuspensePanel();
172 - });
173 -
170 store.addListener('settingsUpdated', (hookSettings, componentFilters) => {
171 chrome.storage.local.set({...hookSettings, componentFilters});
172 });
@@ -565,8 +561,7 @@ function mountReactDevTools() {
561 createProfilerPanel();
562 createSourcesEditorPanel();
563 createElementsInspectPanel();
568 - // Suspense Tab is created via the hook
569 - // TODO(enableSuspenseTab): Create eagerly once Suspense tab is stable
564 + createSuspensePanel();
565 }
566
567 let reactPollingInstance = null;
packages/react-devtools-shared/src/backend/agent.js
+1 -11
@@ -38,7 +38,7 @@ import type {
38 ElementType,
39 } from 'react-devtools-shared/src/frontend/types';
40 import type {GroupItem} from './views/TraceUpdates/canvas';
41 -import {gte, isReactNativeEnvironment} from './utils';
41 +import {isReactNativeEnvironment} from './utils';
42 import {
43 sessionStorageGetItem,
44 sessionStorageRemoveItem,
@@ -961,16 +961,6 @@ export default class Agent extends EventEmitter<{
961
962 rendererInterface.setTraceUpdatesEnabled(this._traceUpdatesEnabled);
963
964 - const renderer = rendererInterface.renderer;
965 - if (renderer !== null) {
966 - const devRenderer = renderer.bundleType === 1;
967 - const enableSuspenseTab =
968 - devRenderer && gte(renderer.version, '19.3.0-canary');
969 - if (enableSuspenseTab) {
970 - this._bridge.send('enableSuspenseTab');
971 - }
972 - }
973 -
964 // When the renderer is attached, we need to tell it whether
965 // we remember the previous selection that we'd like to restore.
966 // It'll start tracking mounts for matches to the last selection path.
packages/react-devtools-shared/src/bridge.js
-1
@@ -199,7 +199,6 @@ export type BackendEvents = {
199 backendInitialized: [],
200 backendVersion: [string],
201 bridgeProtocol: [BridgeProtocol],
202 - enableSuspenseTab: [],
202 extensionBackendInitialized: [],
203 fastRefreshScheduled: [],
204 getSavedPreferences: [],
packages/react-devtools-shared/src/devtools/store.js
-13
@@ -143,7 +143,6 @@ export default class Store extends EventEmitter<{
143 backendVersion: [],
144 collapseNodesByDefault: [],
145 componentFilters: [],
146 - enableSuspenseTab: [],
146 error: [Error],
147 hookSettings: [$ReadOnly<DevToolsHookSettings>],
148 hostInstanceSelected: [Element['id'] | null],
@@ -239,8 +238,6 @@ export default class Store extends EventEmitter<{
238 _supportsClickToInspect: boolean = false;
239 _supportsTimeline: boolean = false;
240 _supportsTraceUpdates: boolean = false;
242 - // Dynamically set if the renderer supports the Suspense tab.
243 - _supportsSuspenseTab: boolean = false;
241
242 _isReloadAndProfileFrontendSupported: boolean = false;
243 _isReloadAndProfileBackendSupported: boolean = false;
@@ -341,7 +338,6 @@ export default class Store extends EventEmitter<{
338 bridge.addListener('hookSettings', this.onHookSettings);
339 bridge.addListener('backendInitialized', this.onBackendInitialized);
340 bridge.addListener('selectElement', this.onHostInstanceSelected);
344 - bridge.addListener('enableSuspenseTab', this.onEnableSuspenseTab);
341 }
342
343 // This is only used in tests to avoid memory leaks.
@@ -2394,15 +2390,6 @@ export default class Store extends EventEmitter<{
2390 }
2391 }
2392
2397 - get supportsSuspenseTab(): boolean {
2398 - return this._supportsSuspenseTab;
2399 - }
2400 -
2401 - onEnableSuspenseTab = (): void => {
2402 - this._supportsSuspenseTab = true;
2403 - this.emit('enableSuspenseTab');
2404 - };
2405 -
2393 // The Store should never throw an Error without also emitting an event.
2394 // Otherwise Store errors will be invisible to users,
2395 // but the downstream errors they cause will be reported as bugs.
packages/react-devtools-shared/src/devtools/views/DevTools.js
+10 -28
@@ -135,21 +135,7 @@ const suspenseTab = {
135 title: 'React Suspense',
136 };
137
138 -const defaultTabs = [componentsTab, profilerTab];
139 -const tabsWithSuspense = [componentsTab, profilerTab, suspenseTab];
140 -
141 -function useIsSuspenseTabEnabled(store: Store): boolean {
142 - const subscribe = useCallback(
143 - (onStoreChange: () => void) => {
144 - store.addListener('enableSuspenseTab', onStoreChange);
145 - return () => {
146 - store.removeListener('enableSuspenseTab', onStoreChange);
147 - };
148 - },
149 - [store],
150 - );
151 - return React.useSyncExternalStore(subscribe, () => store.supportsSuspenseTab);
152 -}
138 +const tabs = [componentsTab, profilerTab, suspenseTab];
139
140 export default function DevTools({
141 bridge,
@@ -183,8 +169,6 @@ export default function DevTools({
169 LOCAL_STORAGE_DEFAULT_TAB_KEY,
170 defaultTab,
171 );
186 - const enableSuspenseTab = useIsSuspenseTabEnabled(store);
187 - const tabs = enableSuspenseTab ? tabsWithSuspense : defaultTabs;
172
173 let tab = currentTab;
174
@@ -364,17 +348,15 @@ export default function DevTools({
348 }
349 />
350 </div>
367 - {enableSuspenseTab && (
368 - <div
369 - className={styles.TabContent}
370 - hidden={tab !== 'suspense'}>
371 - <SuspenseTab
372 - portalContainer={
373 - suspensePortalContainer
374 - }
375 - />
376 - </div>
377 - )}
351 + <div
352 + className={styles.TabContent}
353 + hidden={tab !== 'suspense'}>
354 + <SuspenseTab
355 + portalContainer={
356 + suspensePortalContainer
357 + }
358 + />
359 + </div>
360 </div>
361 {editorPortalContainer ? (
362 <EditorPane