You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
IconButton, Tooltip: reduce per-instance cost of the built-in tooltip #8719
Part of #8676 (P1). Measure with #8714 before and after. This was blocked on #8562, which has merged.
Problem
Button/IconButton.tsx:60 wraps every labelled IconButton in a TooltipV2 unless unsafeDisableTooltip is set. Each tooltip instance:
Registers a global Escape handler through useOnEscapePress (TooltipV2/Tooltip.tsx:289), even while the tooltip is closed.
Runs an accessibility DOM scan on mount in production (:237-288). warning and invariant are no-ops in production, but their arguments are still computed.
hooks/useOnEscapePress.ts:75 and :82 also call Object.keys(registry) on every register and unregister.
Measured: median of 5 runs, N IconButtons:
N
mount, tooltip
mount, none
unmount, tooltip
unmount, none
300
15 ms
9 ms
1.3 ms
0.3 ms
1000
48 ms
26 ms
10.1 ms
0.8 ms
3000
200 ms
73 ms
75.0 ms
2.8 ms
Unmount cost grows super-linearly, about 58x for 10x the buttons. Mount cost grows slightly faster than linear. At a few hundred buttons the cost is small.
Proposed fix
Subscribe to Escape only while the tooltip is open.
Wrap the accessibility scan in __DEV__.
Replace Object.keys(registry).length with a counter.
Don't lazy-mount the tooltip element, because aria-labelledby needs it in the DOM. The fix must work on React 18 and 19.
Acceptance criteria
A closed tooltip doesn't register an Escape handler.
The accessibility scan doesn't run in production builds.
Escape still closes an open tooltip, and the existing Escape ordering tests still pass.
aria-labelledby and aria-describedby wiring doesn't change.
Activity