aidenybai / aidenybai/react-scan

React Review Audit

Aperta
#446 0 commenti 0 reazioni 0 assegnatari Vedi su GitHub
Lingua principale
TypeScript
Stelle
21.8k
Fork
390
Merge medio
23m
PR unite (30g)
1

Descrizione

10 errors, ⚠️ 228 warnings
81 score

Copy as prompt

```text
Fix the following React Review diagnostics in my codebase.

## Errors (10)

1. [error] jsx-key — packages/scan/src/web/views/notifications/collapsed-event.tsx:165
Missing "key" prop for element in iterator.

2. [error] no-mutable-in-deps — packages/scan/src/web/views/notifications/popover.tsx:73
"triggerRef.current" in deps — refs are mutable and don't trigger re-renders, so React won't re-run this effect when it changes. Read the ref inside the effect body instead

3. [error] effect-needs-cleanup — packages/scan/src/web/views/notifications/notifications.tsx:227
useEffect schedules `setTimeout(...)` but never returns a cleanup — leaks the registration on every re-run and on unmount. Return a cleanup function that calls clearTimeout(...)

4. [error] effect-needs-cleanup — packages/scan/src/web/views/notifications/slowdown-history.tsx:27
useEffect schedules `setTimeout(...)` but never returns a cleanup — leaks the registration on every re-run and on unmount. Return a cleanup function that calls clearTimeout(...)

5. [error] effect-needs-cleanup — packages/scan/src/web/views/notifications/slowdown-history.tsx:339
useEffect schedules `setTimeout(...)` but never returns a cleanup — leaks the registration on every re-run and on unmount. Return a cleanup function that calls clearTimeout(...)

6. [error] nextjs-async-client-component — packages/website/public/auto.global.js:2
Async client component "B" — client components cannot be async

7. [error] nextjs-async-client-component — packages/website/public/auto.global.js:2
Async client component "Pa" — client components cannot be async

8. [error] nextjs-async-client-component — packages/website/public/auto.global.js:2
Async client component "Ul" — client components cannot be async

9. [error] nextjs-async-client-component — packages/website/public/auto.global.js:2
Async client component "Jl" — client components cannot be async

10. [error] nextjs-async-client-component — packages/website/public/auto.global.js:2
Async client component "Tc" — client components cannot be async

## Warnings (228)

11. [warning] no-cascading-set-state — kitchen-sink/src/examples/sierpinski/index.tsx:27
3 setState calls in a single useEffect — consider using useReducer or deriving state

12. [warning] click-events-have-key-events — kitchen-sink/src/examples/e2e-fixture/index.tsx:46
Enforce a clickable non-interactive element has at least one keyboard event listener.

13. [warning] no-static-element-interactions — kitchen-sink/src/examples/e2e-fixture/index.tsx:46
Static HTML elements with event handlers require a role.

14. [warning] no-react19-deprecated-apis — kitchen-sink/src/examples/e2e-fixture/index.tsx:1
useContext is superseded by `use()` on React 19+ — `use()` reads context conditionally inside hooks, branches, and loops; switch to `import { use } from 'react'`

15. [warning] no-uncontrolled-input — kitchen-sink/src/examples/todo-list/index.tsx:92
with no `onChange` or `readOnly` — React renders this as a silently read-only field

16. [warning] rerender-functional-setstate — kitchen-sink/src/examples/todo-list/index.tsx:88
setIndex(index + ...) — use functional update to avoid stale closures

17. [warning] iframe-has-title — kitchen-sink/src/main.tsx:39
Missing `title` attribute for the `iframe` element.

18. [warning] design-no-default-tailwind-palette — kitchen-sink/src/main.tsx:19
border-gray-950 reads as the Tailwind template default — use zinc (true neutral), neutral (warmer), or stone (warmest)

19. [warning] design-no-bold-heading — kitchen-sink/src/main.tsx:20
font-bold on

crushes counter shapes at display sizes — use font-semibold (600) or font-medium (500)

20. [warning] design-no-default-tailwind-palette — kitchen-sink/src/main.tsx:24
border-gray-950 reads as the Tailwind template default — use zinc (true neutral), neutral (warmer), or stone (warmest)

21. [warning] design-no-default-tailwind-palette — kitchen-sink/src/main.tsx:29
border-gray-950 reads as the Tailwind template default — use zinc (true neutral), neutral (warmer), or stone (warmest)

22. [warning] js-batch-dom-css — packages/extension/src/inject/notification.ts:36
Multiple sequential element.style assignments — batch with cssText or classList for fewer reflows

23. [warning] js-batch-dom-css — packages/extension/src/inject/notification.ts:130
Multiple sequential element.style assignments — batch with cssText or classList for fewer reflows

24. [warning] js-set-map-lookups — packages/scan/src/core/index.ts:299
array.includes() in a loop is O(n) per call — convert to a Set for O(1) lookups

25. [warning] js-batch-dom-css — packages/scan/src/core/notifications/outline-overlay.ts:172
Multiple sequential element.style assignments — batch with cssText or classList for fewer reflows

26. [warning] js-batch-dom-css — packages/scan/src/core/notifications/outline-overlay.ts:176
Multiple sequential element.style assignments — batch with cssText or classList for fewer reflows

27. [warning] js-batch-dom-css — packages/scan/src/core/notifications/outline-overlay.ts:177
Multiple sequential element.style assignments — batch with cssText or classList for fewer reflows

28. [warning] js-batch-dom-css — packages/scan/src/core/notifications/outline-overlay.ts:178
Multiple sequential element.style assignments — batch with cssText or classList for fewer reflows

29. [warning] js-batch-dom-css — packages/scan/src/core/notifications/outline-overlay.ts:179
Multiple sequential element.style assignments — batch with cssText or classList for fewer reflows

30. [warning] js-batch-dom-css — packages/scan/src/core/notifications/outline-overlay.ts:195
Multiple sequential element.style assignments — batch with cssText or classList for fewer reflows

31. [warning] no-pure-black-background — packages/scan/src/web/toolbar.tsx:22
Pure black background (bg-black) looks harsh — use a near-black tinted toward your brand hue (e.g. bg-gray-950)

32. [warning] no-pure-black-background — packages/scan/src/web/toolbar.tsx:27
Pure black background (bg-black) looks harsh — use a near-black tinted toward your brand hue (e.g. bg-gray-950)

33. [warning] no-giant-component — packages/scan/src/web/views/inspector/components-tree/index.tsx:386
Component "ComponentsTree" is 792 lines — consider breaking it into smaller focused components

34. [warning] design-no-redundant-size-axes — packages/scan/src/web/views/inspector/components-tree/index.tsx:1072
w-4 h-4 → use the shorthand size-4 (Tailwind v3.4+)

35. [warning] design-no-redundant-size-axes — packages/scan/src/web/views/inspector/components-tree/index.tsx:1086
w-4 h-4 → use the shorthand size-4 (Tailwind v3.4+)

36. [warning] design-no-redundant-size-axes — packages/scan/src/web/views/inspector/components-tree/index.tsx:1102
w-4 h-4 → use the shorthand size-4 (Tailwind v3.4+)

37. [warning] async-await-in-loop — packages/scan/src/new-outlines/index.ts:153
await inside a while-loop runs the calls sequentially — for independent operations, collect them and use `await Promise.all(items.map(...))` to run them concurrently

38. [warning] js-batch-dom-css — packages/scan/src/new-outlines/index.ts:304
Multiple sequential element.style assignments — batch with cssText or classList for fewer reflows

39. [warning] js-batch-dom-css — packages/scan/src/new-outlines/index.ts:305
Multiple sequential element.style assignments — batch with cssText or classList for fewer reflows

40. [warning] js-batch-dom-css — packages/scan/src/new-outlines/index.ts:306
Multiple sequential element.style assignments — batch with cssText or classList for fewer reflows

41. [warning] js-batch-dom-css — packages/scan/src/new-outlines/index.ts:307
Multiple sequential element.style assignments — batch with cssText or classList for fewer reflows

42. [warning] js-batch-dom-css — packages/scan/src/new-outlines/index.ts:318
Multiple sequential element.style assignments — batch with cssText or classList for fewer reflows

43. [warning] js-batch-dom-css — packages/scan/src/new-outlines/index.ts:381
Multiple sequential element.style assignments — batch with cssText or classList for fewer reflows

44. [warning] client-passive-event-listeners — packages/scan/src/new-outlines/index.ts:407
"scroll" listener without { passive: true } — blocks scrolling performance. Only add { passive: true } if the handler does NOT call event.preventDefault() (passive listeners silently ignore preventDefault())

45. [warning] rerender-transitions-scroll — packages/scan/src/new-outlines/index.ts:411
setState in a "scroll" handler triggers re-renders at scroll/pointer frequency — wrap in startTransition (mark as non-urgent), use useDeferredValue, or stash in a ref + rAF throttle

46. [warning] js-flatmap-filter — packages/scan/src/web/views/inspector/timeline/utils.ts:148
.map().filter(Boolean) iterates twice — use .flatMap() to transform and filter in a single pass

47. [warning] no-cascading-set-state — packages/scan/src/web/views/notifications/collapsed-event.tsx:36
11 setState calls in a single useEffect — consider using useReducer or deriving state

48. [warning] js-set-map-lookups — packages/scan/src/web/views/inspector/utils.ts:1357
array.includes() in a loop is O(n) per call — convert to a Set for O(1) lookups

49. [warning] js-set-map-lookups — packages/scan/src/web/views/inspector/utils.ts:1394
array.includes() in a loop is O(n) per call — convert to a Set for O(1) lookups

50. [warning] js-set-map-lookups — packages/scan/src/web/views/inspector/utils.ts:1446
array.includes() in a loop is O(n) per call — convert to a Set for O(1) lookups

51. [warning] js-set-map-lookups — packages/scan/src/web/views/inspector/utils.ts:1865
array.indexOf() in a loop is O(n) per call — convert to a Set for O(1) lookups

52. [warning] js-set-map-lookups — packages/scan/src/web/views/inspector/utils.ts:1888
array.includes() in a loop is O(n) per call — convert to a Set for O(1) lookups

53. [warning] js-tosorted-immutable — packages/scan/src/polyfills.ts:4
[...array].sort() — use array.toSorted() for immutable sorting (ES2023)

54. [warning] no-unknown-property — packages/scan/src/web/components/svg-sprite/index.tsx:5
Unknown property found

55. [warning] no-unknown-property — packages/scan/src/web/components/svg-sprite/index.tsx:5
Unknown property found

56. [warning] no-unknown-property — packages/scan/src/web/components/svg-sprite/index.tsx:5
Unknown property found

57. [warning] no-unknown-property — packages/scan/src/web/components/svg-sprite/index.tsx:18
Unknown property found

58. [warning] no-unknown-property — packages/scan/src/web/components/svg-sprite/index.tsx:18
Unknown property found

59. [warning] no-unknown-property — packages/scan/src/web/components/svg-sprite/index.tsx:18
Unknown property found

60. [warning] no-unknown-property — packages/scan/src/web/components/svg-sprite/index.tsx:23
Unknown property found

61. [warning] no-unknown-property — packages/scan/src/web/components/svg-sprite/index.tsx:23
Unknown property found

62. [warning] no-unknown-property — packages/scan/src/web/components/svg-sprite/index.tsx:23
Unknown property found

63. [warning] no-unknown-property — packages/scan/src/web/components/svg-sprite/index.tsx:27
Unknown property found

64. [warning] no-unknown-property — packages/scan/src/web/components/svg-sprite/index.tsx:27
Unknown property found

65. [warning] no-unknown-property — packages/scan/src/web/components/svg-sprite/index.tsx:27
Unknown property found

66. [warning] no-unknown-property — packages/scan/src/web/components/svg-sprite/index.tsx:31
Unknown property found

67. [warning] no-unknown-property — packages/scan/src/web/components/svg-sprite/index.tsx:31
Unknown property found

68. [warning] no-unknown-property — packages/scan/src/web/components/svg-sprite/index.tsx:31
Unknown property found

69. [warning] no-unknown-property — packages/scan/src/web/components/svg-sprite/index.tsx:36
Unknown property found

70. [warning] no-unknown-property — packages/scan/src/web/components/svg-sprite/index.tsx:36
Unknown property found

71. [warning] no-unknown-property — packages/scan/src/web/components/svg-sprite/index.tsx:36
Unknown property found

72. [warning] no-unknown-property — packages/scan/src/web/components/svg-sprite/index.tsx:45
Unknown property found

73. [warning] no-unknown-property — packages/scan/src/web/components/svg-sprite/index.tsx:45
Unknown property found

74. [warning] no-unknown-property — packages/scan/src/web/components/svg-sprite/index.tsx:45
Unknown property found

75. [warning] no-unknown-property — packages/scan/src/web/components/svg-sprite/index.tsx:51
Unknown property found

76. [warning] no-unknown-property — packages/scan/src/web/components/svg-sprite/index.tsx:51
Unknown property found

77. [warning] no-unknown-property — packages/scan/src/web/components/svg-sprite/index.tsx:51
Unknown property found

78. [warning] no-unknown-property — packages/scan/src/web/components/svg-sprite/index.tsx:56
Unknown property found

79. [warning] no-unknown-property — packages/scan/src/web/components/svg-sprite/index.tsx:56
Unknown property found

80. [warning] no-unknown-property — packages/scan/src/web/components/svg-sprite/index.tsx:56
Unknown property found

81. [warning] no-unknown-property — packages/scan/src/web/components/svg-sprite/index.tsx:60
Unknown property found

82. [warning] no-unknown-property — packages/scan/src/web/components/svg-sprite/index.tsx:60
Unknown property found

83. [warning] no-unknown-property — packages/scan/src/web/components/svg-sprite/index.tsx:60
Unknown property found

84. [warning] no-unknown-property — packages/scan/src/web/components/svg-sprite/index.tsx:64
Unknown property found

85. [warning] no-unknown-property — packages/scan/src/web/components/svg-sprite/index.tsx:64
Unknown property found

86. [warning] no-unknown-property — packages/scan/src/web/components/svg-sprite/index.tsx:64
Unknown property found

87. [warning] no-unknown-property — packages/scan/src/web/components/svg-sprite/index.tsx:72
Unknown property found

88. [warning] no-unknown-property — packages/scan/src/web/components/svg-sprite/index.tsx:72
Unknown property found

89. [warning] no-unknown-property — packages/scan/src/web/components/svg-sprite/index.tsx:72
Unknown property found

90. [warning] no-unknown-property — packages/scan/src/web/components/svg-sprite/index.tsx:78
Unknown property found

91. [warning] no-unknown-property — packages/scan/src/web/components/svg-sprite/index.tsx:78
Unknown property found

92. [warning] no-unknown-property — packages/scan/src/web/components/svg-sprite/index.tsx:78
Unknown property found

93. [warning] no-unknown-property — packages/scan/src/web/components/svg-sprite/index.tsx:84
Unknown property found

94. [warning] no-unknown-property — packages/scan/src/web/components/svg-sprite/index.tsx:84
Unknown property found

95. [warning] no-unknown-property — packages/scan/src/web/components/svg-sprite/index.tsx:84
Unknown property found

96. [warning] no-unknown-property — packages/scan/src/web/components/svg-sprite/index.tsx:89
Unknown property found

97. [warning] no-unknown-property — packages/scan/src/web/components/svg-sprite/index.tsx:89
Unknown property found

98. [warning] no-unknown-property — packages/scan/src/web/components/svg-sprite/index.tsx:89
Unknown property found

99. [warning] no-unknown-property — packages/scan/src/web/components/svg-sprite/index.tsx:94
Unknown property found

100. [warning] no-unknown-property — packages/scan/src/web/components/svg-sprite/index.tsx:94
Unknown property found

101. [warning] no-unknown-property — packages/scan/src/web/components/svg-sprite/index.tsx:94
Unknown property found

102. [warning] no-unknown-property — packages/scan/src/web/components/svg-sprite/index.tsx:99
Unknown property found

103. [warning] no-unknown-property — packages/scan/src/web/components/svg-sprite/index.tsx:99
Unknown property found

104. [warning] no-unknown-property — packages/scan/src/web/components/svg-sprite/index.tsx:99
Unknown property found

105. [warning] no-unknown-property — packages/scan/src/web/components/svg-sprite/index.tsx:104
Unknown property found

106. [warning] no-unknown-property — packages/scan/src/web/components/svg-sprite/index.tsx:104
Unknown property found

107. [warning] no-unknown-property — packages/scan/src/web/components/svg-sprite/index.tsx:104
Unknown property found

108. [warning] js-cache-property-access — packages/scan/src/web/views/inspector/whats-changed/use-change-store.ts:123
aggregatedChanges.contextChanges.set is read 3 times inside this loop — hoist into a const at the top of the loop body

109. [warning] js-cache-property-access — packages/scan/src/web/views/inspector/whats-changed/use-change-store.ts:128
change.contextType.toString is read 3 times inside this loop — hoist into a const at the top of the loop body

110. [warning] design-no-default-tailwind-palette — packages/scan/src/web/views/inspector/diff-value.tsx:55
text-gray-500 reads as the Tailwind template default — use zinc (true neutral), neutral (warmer), or stone (warmest)

111. [warning] design-no-default-tailwind-palette — packages/scan/src/web/views/inspector/diff-value.tsx:81
text-gray-500 reads as the Tailwind template default — use zinc (true neutral), neutral (warmer), or stone (warmest)

112. [warning] design-no-default-tailwind-palette — packages/scan/src/web/views/inspector/diff-value.tsx:118
text-gray-500 reads as the Tailwind template default — use zinc (true neutral), neutral (warmer), or stone (warmest)

113. [warning] no-array-index-as-key — packages/scan/src/web/views/inspector/diff-value.tsx:143
Array index "index" used as key — causes bugs when list is reordered or filtered

114. [warning] js-flatmap-filter — packages/scan/src/core/notifications/event-tracking.ts:290
.map().filter(Boolean) iterates twice — use .flatMap() to transform and filter in a single pass

115. [warning] no-giant-component — packages/scan/src/web/views/inspector/overlay/index.tsx:48
Component "ScanOverlay" is 684 lines — consider breaking it into smaller focused components

116. [warning] no-cascading-set-state — packages/scan/src/web/views/inspector/what-changed.tsx:36
3 setState calls in a single useEffect — consider using useReducer or deriving state

117. [warning] js-combine-iterations — packages/scan/src/web/views/inspector/what-changed.tsx:49
.filter().map() iterates the array twice — combine into a single loop with .reduce() or for...of

118. [warning] no-render-in-render — packages/scan/src/web/views/inspector/what-changed.tsx:323
Inline render function "renderName()" — extract to a separate component for proper reconciliation

119. [warning] no-array-index-as-key — packages/scan/src/web/views/inspector/what-changed.tsx:452
Array index "i" used as key — causes bugs when list is reordered or filtered

120. [warning] js-batch-dom-css — packages/scan/src/web/views/inspector/flash-overlay.ts:64
Multiple sequential element.style assignments — batch with cssText or classList for fewer reflows

121. [warning] js-batch-dom-css — packages/scan/src/web/views/inspector/flash-overlay.ts:68
Multiple sequential element.style assignments — batch with cssText or classList for fewer reflows

122. [warning] no-unknown-property — packages/scan/src/web/views/notifications/icons.tsx:18
Unknown property found

123. [warning] no-unknown-property — packages/scan/src/web/views/notifications/icons.tsx:19
Unknown property found

124. [warning] no-unknown-property — packages/scan/src/web/views/notifications/icons.tsx:20
Unknown property found

125. [warning] no-unknown-property — packages/scan/src/web/views/notifications/icons.tsx:78
Unknown property found

126. [warning] no-unknown-property — packages/scan/src/web/views/notifications/icons.tsx:79
Unknown property found

127. [warning] no-unknown-property — packages/scan/src/web/views/notifications/icons.tsx:80
Unknown property found

128. [warning] no-unknown-property — packages/scan/src/web/views/notifications/icons.tsx:123
Unknown property found

129. [warning] no-unknown-property — packages/scan/src/web/views/notifications/icons.tsx:124
Unknown property found

130. [warning] no-unknown-property — packages/scan/src/web/views/notifications/icons.tsx:125
Unknown property found

131. [warning] no-unknown-property — packages/scan/src/web/views/notifications/icons.tsx:143
Unknown property found

132. [warning] no-unknown-property — packages/scan/src/web/views/notifications/icons.tsx:144
Unknown property found

133. [warning] no-unknown-property — packages/scan/src/web/views/notifications/icons.tsx:145
Unknown property found

134. [warning] no-unknown-property — packages/scan/src/web/views/notifications/icons.tsx:165
Unknown property found

135. [warning] no-unknown-property — packages/scan/src/web/views/notifications/icons.tsx:166
Unknown property found

136. [warning] no-unknown-property — packages/scan/src/web/views/notifications/icons.tsx:167
Unknown property found

137. [warning] no-unknown-property — packages/scan/src/web/views/notifications/icons.tsx:192
Unknown property found

138. [warning] no-unknown-property — packages/scan/src/web/views/notifications/icons.tsx:193
Unknown property found

139. [warning] no-unknown-property — packages/scan/src/web/views/notifications/icons.tsx:194
Unknown property found

140. [warning] no-unknown-property — packages/scan/src/web/views/notifications/icons.tsx:213
Unknown property found

141. [warning] no-unknown-property — packages/scan/src/web/views/notifications/icons.tsx:214
Unknown property found

142. [warning] no-unknown-property — packages/scan/src/web/views/notifications/icons.tsx:215
Unknown property found

143. [warning] no-unknown-property — packages/scan/src/web/views/notifications/icons.tsx:237
Unknown property found

144. [warning] no-unknown-property — packages/scan/src/web/views/notifications/icons.tsx:238
Unknown property found

145. [warning] no-unknown-property — packages/scan/src/web/views/notifications/icons.tsx:239
Unknown property found

146. [warning] no-unknown-property — packages/scan/src/web/views/notifications/icons.tsx:265
Unknown property found

147. [warning] no-unknown-property — packages/scan/src/web/views/notifications/icons.tsx:266
Unknown property found

148. [warning] no-unknown-property — packages/scan/src/web/views/notifications/icons.tsx:267
Unknown property found

149. [warning] rerender-memo-with-default-value — packages/scan/src/web/views/notifications/icons.tsx:55
Default prop value [] creates a new array reference every render — extract to a module-level constant

150. [warning] no-unknown-property — packages/scan/src/web/widget/resize-handle.tsx:349
Unknown property found

151. [warning] no-giant-component — packages/scan/src/web/widget/resize-handle.tsx:26
Component "ResizeHandle" is 353 lines — consider breaking it into smaller focused components

152. [warning] rerender-lazy-state-init — packages/scan/src/web/views/notifications/render-explanation.tsx:16
useState(getIsProduction()) calls initializer on every render — use useState(() => getIsProduction()) for lazy initialization

153. [warning] no-cascading-set-state — packages/scan/src/web/views/notifications/popover.tsx:75
4 setState calls in a single useEffect — consider using useReducer or deriving state

154. [warning] no-permanent-will-change — packages/scan/src/web/views/notifications/popover.tsx:167
Permanent will-change wastes GPU memory — apply only during active animation and remove after

155. [warning] rerender-lazy-state-init — packages/scan/src/web/views/notifications/render-bar-chart.tsx:67
useState(getIsProduction()) calls initializer on every render — use useState(() => getIsProduction()) for lazy initialization

156. [warning] no-giant-component — packages/scan/src/web/views/notifications/render-bar-chart.tsx:184
Component "RenderBar" is 345 lines — consider breaking it into smaller focused components

157. [warning] rendering-hydration-mismatch-time — packages/scan/src/web/views/notifications/render-bar-chart.tsx:261
Date.now() reachable from JSX renders differently on server vs client — wrap in useEffect+useState (client-only) or add suppressHydrationWarning to the parent if intentional

158. [warning] rendering-hydration-mismatch-time — packages/scan/src/web/views/notifications/render-bar-chart.tsx:365
Date.now() reachable from JSX renders differently on server vs client — wrap in useEffect+useState (client-only) or add suppressHydrationWarning to the parent if intentional

159. [warning] no-array-index-as-key — packages/scan/src/web/views/notifications/render-bar-chart.tsx:502
Array index "i" used as key — causes bugs when list is reordered or filtered

160. [warning] js-combine-iterations — packages/scan/src/web/views/toolbar/index.tsx:194
.filter().map() iterates the array twice — combine into a single loop with .reduce() or for...of

161. [warning] js-combine-iterations — packages/scan/src/web/views/notifications/notifications.tsx:34
.filter().map() iterates the array twice — combine into a single loop with .reduce() or for...of

162. [warning] js-combine-iterations — packages/scan/src/web/views/notifications/notifications.tsx:43
.filter().map() iterates the array twice — combine into a single loop with .reduce() or for...of

163. [warning] js-combine-iterations — packages/scan/src/web/views/notifications/notifications.tsx:51
.filter().map() iterates the array twice — combine into a single loop with .reduce() or for...of

164. [warning] rendering-hydration-mismatch-time — packages/scan/src/web/views/notifications/notifications.tsx:441
Date.now() reachable from JSX renders differently on server vs client — wrap in useEffect+useState (client-only) or add suppressHydrationWarning to the parent if intentional

165. [warning] rendering-hydration-mismatch-time — packages/scan/src/web/views/notifications/notifications.tsx:464
Date.now() reachable from JSX renders differently on server vs client — wrap in useEffect+useState (client-only) or add suppressHydrationWarning to the parent if intentional

166. [warning] no-giant-component — packages/scan/src/web/widget/index.tsx:44
Component "Widget" is 715 lines — consider breaking it into smaller focused components

167. [warning] rerender-state-only-in-handlers — packages/scan/src/web/widget/index.tsx:653
useState "_" is updated but never read in the component's return — use useRef so updates don't trigger re-renders

168. [warning] js-batch-dom-css — packages/scan/src/web/widget/index.tsx:574
Multiple sequential element.style assignments — batch with cssText or classList for fewer reflows

169. [warning] js-batch-dom-css — packages/scan/src/web/widget/index.tsx:606
Multiple sequential element.style assignments — batch with cssText or classList for fewer reflows

170. [warning] js-batch-dom-css — packages/scan/src/web/widget/index.tsx:607
Multiple sequential element.style assignments — batch with cssText or classList for fewer reflows

171. [warning] rendering-hydration-no-flicker — packages/scan/src/web/widget/index.tsx:654
useEffect(setState, []) on mount causes a flash — consider useSyncExternalStore or suppressHydrationWarning

172. [warning] no-cascading-set-state — packages/scan/src/web/views/notifications/slowdown-history.tsx:27
3 setState calls in a single useEffect — consider using useReducer or deriving state

173. [warning] no-cascading-set-state — packages/scan/src/web/views/notifications/slowdown-history.tsx:59
3 setState calls in a single useEffect — consider using useReducer or deriving state

174. [warning] async-await-in-loop — packages/scan/tsup.config.ts:20
await inside a for…of loop runs the calls sequentially — for independent operations, collect them and use `await Promise.all(items.map(...))` to run them concurrently

175. [warning] async-parallel — packages/scan/tsup.config.ts:144
3 sequential await statements that appear independent — use Promise.all() for parallel execution

176. [warning] server-fetch-without-revalidate — packages/website/app/api/waitlist/route.ts:38
fetch("https://app.loops.so/api/v1/contacts/create") in a Server Component / route handler defaults to forever-caching — pass { next: { revalidate: } } / { next: { tags: [...] } } / { cache: "no-store" } so stale data doesn't quietly persist

177. [warning] no-unknown-property — packages/website/app/replay/page.tsx:183
Unknown property found

178. [warning] click-events-have-key-events — packages/website/app/replay/page.tsx:136
Enforce a clickable non-interactive element has at least one keyboard event listener.

179. [warning] click-events-have-key-events — packages/website/app/replay/page.tsx:162
Enforce a clickable non-interactive element has at least one keyboard event listener.

180. [warning] click-events-have-key-events — packages/website/app/replay/page.tsx:172
Enforce a clickable non-interactive element has at least one keyboard event listener.

181. [warning] no-static-element-interactions — packages/website/app/replay/page.tsx:136
Static HTML elements with event handlers require a role.

182. [warning] no-static-element-interactions — packages/website/app/replay/page.tsx:162
Static HTML elements with event handlers require a role.

183. [warning] no-static-element-interactions — packages/website/app/replay/page.tsx:172
Static HTML elements with event handlers require a role.

184. [warning] nextjs-missing-metadata — packages/website/app/replay/page.tsx:1
Page without metadata or generateMetadata export — hurts SEO

185. [warning] prefer-useReducer — packages/website/app/replay/page.tsx:5
Component "ReplayPage" has 6 useState calls — consider useReducer for related state

186. [warning] design-no-default-tailwind-palette — packages/website/app/replay/page.tsx:86
border-gray-200 reads as the Tailwind template default — use zinc (true neutral), neutral (warmer), or stone (warmest)

187. [warning] design-no-default-tailwind-palette — packages/website/app/replay/page.tsx:86
text-gray-900 reads as the Tailwind template default — use zinc (true neutral), neutral (warmer), or stone (warmest)

188. [warning] design-no-default-tailwind-palette — packages/website/app/replay/page.tsx:86
placeholder-gray-500 reads as the Tailwind template default — use zinc (true neutral), neutral (warmer), or stone (warmest)

189. [warning] design-no-redundant-size-axes — packages/website/app/replay/page.tsx:99
w-5 h-5 → use the shorthand size-5 (Tailwind v3.4+)

190. [warning] design-no-redundant-size-axes — packages/website/app/replay/page.tsx:111
w-5 h-5 → use the shorthand size-5 (Tailwind v3.4+)

191. [warning] no-inline-bounce-easing — packages/website/app/replay/page.tsx:115
animate-bounce feels dated and tacky — use a subtle ease-out transform for natural deceleration

192. [warning] design-no-redundant-size-axes — packages/website/app/replay/page.tsx:123
w-5 h-5 → use the shorthand size-5 (Tailwind v3.4+)

193. [warning] design-no-default-tailwind-palette — packages/website/app/replay/page.tsx:132
text-gray-500 reads as the Tailwind template default — use zinc (true neutral), neutral (warmer), or stone (warmest)

194. [warning] design-no-redundant-size-axes — packages/website/app/replay/page.tsx:148
w-16 h-16 → use the shorthand size-16 (Tailwind v3.4+)

195. [warning] design-no-default-tailwind-palette — packages/website/app/replay/page.tsx:149
text-gray-900 reads as the Tailwind template default — use zinc (true neutral), neutral (warmer), or stone (warmest)

196. [warning] design-no-redundant-size-axes — packages/website/app/replay/page.tsx:149
w-7 h-7 → use the shorthand size-7 (Tailwind v3.4+)

197. [warning] no-pure-black-background — packages/website/app/replay/page.tsx:201
Pure black background (bg-black) looks harsh — use a near-black tinted toward your brand hue (e.g. bg-gray-950)

198. [warning] design-no-redundant-size-axes — packages/website/app/replay/page.tsx:205
w-7 h-7 → use the shorthand size-7 (Tailwind v3.4+)

199. [warning] design-no-redundant-size-axes — packages/website/app/replay/page.tsx:207
w-4 h-4 → use the shorthand size-4 (Tailwind v3.4+)

200. [warning] design-no-redundant-size-axes — packages/website/app/replay/page.tsx:215
w-8 h-8 → use the shorthand size-8 (Tailwind v3.4+)

201. [warning] design-no-redundant-size-axes — packages/website/app/replay/page.tsx:217
w-5 h-5 → use the shorthand size-5 (Tailwind v3.4+)

202. [warning] no-pure-black-background — packages/website/app/replay/page.tsx:222
Pure black background (bg-black) looks harsh — use a near-black tinted toward your brand hue (e.g. bg-gray-950)

203. [warning] nextjs-missing-metadata — packages/website/app/page.tsx:1
Page without metadata or generateMetadata export — hurts SEO

204. [warning] design-no-redundant-size-axes — packages/website/app/page.tsx:36
w-[18px] h-[18px] → use the shorthand size-[18px] (Tailwind v3.4+)

205. [warning] design-no-redundant-size-axes — packages/website/app/page.tsx:45
w-[18px] h-[18px] → use the shorthand size-[18px] (Tailwind v3.4+)

206. [warning] no-danger — packages/website/components/install-guide.tsx:260
Do not use `dangerouslySetInnerHTML` prop

207. [warning] no-danger — packages/website/components/install-guide.tsx:284
Do not use `dangerouslySetInnerHTML` prop

208. [warning] rendering-svg-precision — packages/website/components/icons/icon-discord.tsx:15
SVG d attribute uses 4+ decimal precision — truncate to 1–2 decimals to shrink markup with no visible difference

209. [warning] design-no-redundant-size-axes — packages/website/components/header.tsx:43
w-6 h-6 → use the shorthand size-6 (Tailwind v3.4+)

210. [warning] js-tosorted-immutable — packages/website/public/auto.global.js:2
[...array].sort() — use array.toSorted() for immutable sorting (ES2023)

211. [warning] js-set-map-lookups — packages/website/public/auto.global.js:2
array.indexOf() in a loop is O(n) per call — convert to a Set for O(1) lookups

212. [warning] client-passive-event-listeners — packages/website/public/auto.global.js:2
"scroll" listener without { passive: true } — blocks scrolling performance. Only add { passive: true } if the handler does NOT call event.preventDefault() (passive listeners silently ignore preventDefault())

213. [warning] client-passive-event-listeners — packages/website/public/auto.global.js:2
"scroll" listener without { passive: true } — blocks scrolling performance. Only add { passive: true } if the handler does NOT call event.preventDefault() (passive listeners silently ignore preventDefault())

214. [warning] js-cache-property-access — packages/website/public/auto.global.js:2
t.contextChanges.set is read 3 times inside this loop — hoist into a const at the top of the loop body

215. [warning] js-cache-property-access — packages/website/public/auto.global.js:2
n.contextType.toString is read 3 times inside this loop — hoist into a const at the top of the loop body

216. [warning] js-combine-iterations — packages/website/public/auto.global.js:2
.filter().map() iterates the array twice — combine into a single loop with .reduce() or for...of

217. [warning] js-set-map-lookups — packages/website/public/auto.global.js:2
array.includes() in a loop is O(n) per call — convert to a Set for O(1) lookups

218. [warning] js-set-map-lookups — packages/website/public/auto.global.js:2
array.includes() in a loop is O(n) per call — convert to a Set for O(1) lookups

219. [warning] js-set-map-lookups — packages/website/public/auto.global.js:2
array.includes() in a loop is O(n) per call — convert to a Set for O(1) lookups

220. [warning] js-tosorted-immutable — packages/website/public/auto.global.js:2
[...array].sort() — use array.toSorted() for immutable sorting (ES2023)

221. [warning] async-await-in-loop — packages/website/public/auto.global.js:2
await inside a for-loop runs the calls sequentially — for independent operations, collect them and use `await Promise.all(items.map(...))` to run them concurrently

222. [warning] client-passive-event-listeners — packages/website/public/auto.global.js:2
"scroll" listener without { passive: true } — blocks scrolling performance. Only add { passive: true } if the handler does NOT call event.preventDefault() (passive listeners silently ignore preventDefault())

223. [warning] rerender-transitions-scroll — packages/website/public/auto.global.js:2
setState in a "scroll" handler triggers re-renders at scroll/pointer frequency — wrap in startTransition (mark as non-urgent), use useDeferredValue, or stash in a ref + rAF throttle

224. [warning] js-set-map-lookups — packages/website/public/auto.global.js:2
array.indexOf() in a loop is O(n) per call — convert to a Set for O(1) lookups

225. [warning] js-set-map-lookups — packages/website/public/auto.global.js:2
array.includes() in a loop is O(n) per call — convert to a Set for O(1) lookups

226. [warning] js-set-map-lookups — packages/website/public/auto.global.js:2
array.includes() in a loop is O(n) per call — convert to a Set for O(1) lookups

227. [warning] js-set-map-lookups — packages/website/public/auto.global.js:2
array.includes() in a loop is O(n) per call — convert to a Set for O(1) lookups

228. [warning] js-combine-iterations — packages/website/public/auto.global.js:2
.map().filter() iterates the array twice — combine into a single loop with .reduce() or for...of

229. [warning] js-set-map-lookups — packages/website/public/auto.global.js:2
array.indexOf() in a loop is O(n) per call — convert to a Set for O(1) lookups

230. [warning] js-set-map-lookups — packages/website/public/auto.global.js:2
array.indexOf() in a loop is O(n) per call — convert to a Set for O(1) lookups

231. [warning] js-flatmap-filter — packages/website/public/auto.global.js:2
.map().filter(Boolean) iterates twice — use .flatMap() to transform and filter in a single pass

232. [warning] js-combine-iterations — packages/website/public/auto.global.js:2
.filter().forEach() iterates the array twice — combine into a single loop with .reduce() or for...of

233. [warning] js-combine-iterations — packages/website/public/auto.global.js:2
.filter().map() iterates the array twice — combine into a single loop with .reduce() or for...of

234. [warning] js-combine-iterations — packages/website/public/auto.global.js:2
.filter().map() iterates the array twice — combine into a single loop with .reduce() or for...of

235. [warning] js-combine-iterations — packages/website/public/auto.global.js:2
.filter().map() iterates the array twice — combine into a single loop with .reduce() or for...of

236. [warning] js-combine-iterations — packages/website/public/auto.global.js:2
.filter().map() iterates the array twice — combine into a single loop with .reduce() or for...of

237. [warning] client-passive-event-listeners — packages/website/public/auto.global.js:2
"scroll" listener without { passive: true } — blocks scrolling performance. Only add { passive: true } if the handler does NOT call event.preventDefault() (passive listeners silently ignore preventDefault())

238. [warning] js-set-map-lookups — packages/website/public/auto.global.js:2
array.includes() in a loop is O(n) per call — convert to a Set for O(1) lookups
```

---

### ❌ Errors (10)

Async client component "B" — client components cannot be async · 5 in 1 file

nextjs-async-client-component

> Fetch data in a parent Server Component and pass it as props, or use useQuery/useSWR in the client component

| File | Lines |
| --- | --- |
| [`packages/website/public/auto.global.js`](https://github.com/aidenybai/react-scan/blob/78a1431a566a1ae245781386e8ba7c81dba8fe47/packages/website/public/auto.global.js#L2) | 2, 2, 2, 2, 2 |

useEffect schedules `setTimeout(...)` but never returns a cleanup — leaks the registration on every re-run and on unmount. Return a cleanup function that calls clearTimeout(...) · 3 in 2 files

effect-needs-cleanup

> Return a cleanup function that releases the subscription / timer: `return () => target.removeEventListener(name, handler)` for listeners, `return () => clearInterval(id)` / `clearTimeout(id)` for timers, or `return unsubscribe` if the subscribe call already returned one

| File | Lines |
| --- | --- |
| [`packages/scan/src/web/views/notifications/slowdown-history.tsx`](https://github.com/aidenybai/react-scan/blob/78a1431a566a1ae245781386e8ba7c81dba8fe47/packages/scan/src/web/views/notifications/slowdown-history.tsx#L27) | 27, 339 |
| [`packages/scan/src/web/views/notifications/notifications.tsx`](https://github.com/aidenybai/react-scan/blob/78a1431a566a1ae245781386e8ba7c81dba8fe47/packages/scan/src/web/views/notifications/notifications.tsx#L227) | 227 |

Missing "key" prop for element in iterator. · 1 in 1 file

jsx-key

> Add a "key" prop to the element in the iterator (https://react.dev/learn/rendering-lists#keeping-list-items-in-order-with-key).

| File | Lines |
| --- | --- |
| [`packages/scan/src/web/views/notifications/collapsed-event.tsx`](https://github.com/aidenybai/react-scan/blob/78a1431a566a1ae245781386e8ba7c81dba8fe47/packages/scan/src/web/views/notifications/collapsed-event.tsx#L165) | 165 |

"triggerRef.current" in deps — refs are mutable and don't trigger re-renders, so React won't re-run this effect when it changes. Read the ref inside the effect body instead · 1 in 1 file

no-mutable-in-deps

> Read mutable values (`location.pathname`, `ref.current`) inside the effect body instead of in the deps array, or subscribe with `useSyncExternalStore`. Mutations to these don't trigger re-renders, so listing them in deps doesn't make the effect react to changes

| File | Lines |
| --- | --- |
| [`packages/scan/src/web/views/notifications/popover.tsx`](https://github.com/aidenybai/react-scan/blob/78a1431a566a1ae245781386e8ba7c81dba8fe47/packages/scan/src/web/views/notifications/popover.tsx#L73) | 73 |

---

### ⚠️ Warnings (228)

Unknown property found · 83 in 4 files

no-unknown-property

> Use 'strokeWidth' instead

| File | Lines |
| --- | --- |
| [`packages/scan/src/web/components/svg-sprite/index.tsx`](https://github.com/aidenybai/react-scan/blob/78a1431a566a1ae245781386e8ba7c81dba8fe47/packages/scan/src/web/components/svg-sprite/index.tsx#L5) | 5, 5, 5, 18, 18, 18, 23, 23, +46 more |
| [`packages/scan/src/web/views/notifications/icons.tsx`](https://github.com/aidenybai/react-scan/blob/78a1431a566a1ae245781386e8ba7c81dba8fe47/packages/scan/src/web/views/notifications/icons.tsx#L18) | 18, 19, 20, 78, 79, 80, 123, 124, +19 more |
| [`packages/scan/src/web/widget/resize-handle.tsx`](https://github.com/aidenybai/react-scan/blob/78a1431a566a1ae245781386e8ba7c81dba8fe47/packages/scan/src/web/widget/resize-handle.tsx#L349) | 349 |
| [`packages/website/app/replay/page.tsx`](https://github.com/aidenybai/react-scan/blob/78a1431a566a1ae245781386e8ba7c81dba8fe47/packages/website/app/replay/page.tsx#L183) | 183 |

Multiple sequential element.style assignments — batch with cssText or classList for fewer reflows · 19 in 5 files

js-batch-dom-css

> Batch DOM/CSS reads and writes — interleaving them inside a loop causes layout thrashing. Read first, then write

| File | Lines |
| --- | --- |
| [`packages/scan/src/core/notifications/outline-overlay.ts`](https://github.com/aidenybai/react-scan/blob/78a1431a566a1ae245781386e8ba7c81dba8fe47/packages/scan/src/core/notifications/outline-overlay.ts#L172) | 172, 176, 177, 178, 179, 195 |
| [`packages/scan/src/new-outlines/index.ts`](https://github.com/aidenybai/react-scan/blob/78a1431a566a1ae245781386e8ba7c81dba8fe47/packages/scan/src/new-outlines/index.ts#L304) | 304, 305, 306, 307, 318, 381 |
| [`packages/scan/src/web/widget/index.tsx`](https://github.com/aidenybai/react-scan/blob/78a1431a566a1ae245781386e8ba7c81dba8fe47/packages/scan/src/web/widget/index.tsx#L574) | 574, 606, 607 |
| [`packages/extension/src/inject/notification.ts`](https://github.com/aidenybai/react-scan/blob/78a1431a566a1ae245781386e8ba7c81dba8fe47/packages/extension/src/inject/notification.ts#L36) | 36, 130 |
| [`packages/scan/src/web/views/inspector/flash-overlay.ts`](https://github.com/aidenybai/react-scan/blob/78a1431a566a1ae245781386e8ba7c81dba8fe47/packages/scan/src/web/views/inspector/flash-overlay.ts#L64) | 64, 68 |

array.includes() in a loop is O(n) per call — convert to a Set for O(1) lookups · 17 in 3 files

js-set-map-lookups

> Use a `Set` or `Map` for repeated membership tests / keyed lookups — `Array.includes`/`find` is O(n) per call

| File | Lines |
| --- | --- |
| [`packages/website/public/auto.global.js`](https://github.com/aidenybai/react-scan/blob/78a1431a566a1ae245781386e8ba7c81dba8fe47/packages/website/public/auto.global.js#L2) | 2, 2, 2, 2, 2, 2, 2, 2, +3 more |
| [`packages/scan/src/web/views/inspector/utils.ts`](https://github.com/aidenybai/react-scan/blob/78a1431a566a1ae245781386e8ba7c81dba8fe47/packages/scan/src/web/views/inspector/utils.ts#L1357) | 1357, 1394, 1446, 1865, 1888 |
| [`packages/scan/src/core/index.ts`](https://github.com/aidenybai/react-scan/blob/78a1431a566a1ae245781386e8ba7c81dba8fe47/packages/scan/src/core/index.ts#L299) | 299 |

w-4 h-4 → use the shorthand size-4 (Tailwind v3.4+) · 15 in 4 files

design-no-redundant-size-axes

> Collapse `w-N h-N` to `size-N` (Tailwind v3.4+) when both axes match

| File | Lines |
| --- | --- |
| [`packages/website/app/replay/page.tsx`](https://github.com/aidenybai/react-scan/blob/78a1431a566a1ae245781386e8ba7c81dba8fe47/packages/website/app/replay/page.tsx#L99) | 99, 111, 123, 148, 149, 205, 207, 215, +1 more |
| [`packages/scan/src/web/views/inspector/components-tree/index.tsx`](https://github.com/aidenybai/react-scan/blob/78a1431a566a1ae245781386e8ba7c81dba8fe47/packages/scan/src/web/views/inspector/components-tree/index.tsx#L1072) | 1072, 1086, 1102 |
| [`packages/website/app/page.tsx`](https://github.com/aidenybai/react-scan/blob/78a1431a566a1ae245781386e8ba7c81dba8fe47/packages/website/app/page.tsx#L36) | 36, 45 |
| [`packages/website/components/header.tsx`](https://github.com/aidenybai/react-scan/blob/78a1431a566a1ae245781386e8ba7c81dba8fe47/packages/website/components/header.tsx#L43) | 43 |

.filter().map() iterates the array twice — combine into a single loop with .reduce() or for...of · 12 in 4 files

js-combine-iterations

> Combine `.map().filter()` (or similar chains) into a single pass with `.reduce()` or a `for...of` loop to avoid iterating the array twice

| File | Lines |
| --- | --- |
| [`packages/website/public/auto.global.js`](https://github.com/aidenybai/react-scan/blob/78a1431a566a1ae245781386e8ba7c81dba8fe47/packages/website/public/auto.global.js#L2) | 2, 2, 2, 2, 2, 2, 2 |
| [`packages/scan/src/web/views/notifications/notifications.tsx`](https://github.com/aidenybai/react-scan/blob/78a1431a566a1ae245781386e8ba7c81dba8fe47/packages/scan/src/web/views/notifications/notifications.tsx#L34) | 34, 43, 51 |
| [`packages/scan/src/web/views/inspector/what-changed.tsx`](https://github.com/aidenybai/react-scan/blob/78a1431a566a1ae245781386e8ba7c81dba8fe47/packages/scan/src/web/views/inspector/what-changed.tsx#L49) | 49 |
| [`packages/scan/src/web/views/toolbar/index.tsx`](https://github.com/aidenybai/react-scan/blob/78a1431a566a1ae245781386e8ba7c81dba8fe47/packages/scan/src/web/views/toolbar/index.tsx#L194) | 194 |

border-gray-950 reads as the Tailwind template default — use zinc (true neutral), neutral (warmer), or stone (warmest) · 11 in 3 files

design-no-default-tailwind-palette

> Replace `indigo-*` / `gray-*` / `slate-*` with project tokens, your brand color, or a less-default neutral (`zinc`, `neutral`, `stone`)

| File | Lines |
| --- | --- |
| [`packages/website/app/replay/page.tsx`](https://github.com/aidenybai/react-scan/blob/78a1431a566a1ae245781386e8ba7c81dba8fe47/packages/website/app/replay/page.tsx#L86) | 86, 86, 86, 132, 149 |
| [`kitchen-sink/src/main.tsx`](https://github.com/aidenybai/react-scan/blob/78a1431a566a1ae245781386e8ba7c81dba8fe47/kitchen-sink/src/main.tsx#L19) | 19, 24, 29 |
| [`packages/scan/src/web/views/inspector/diff-value.tsx`](https://github.com/aidenybai/react-scan/blob/78a1431a566a1ae245781386e8ba7c81dba8fe47/packages/scan/src/web/views/inspector/diff-value.tsx#L55) | 55, 81, 118 |

3 setState calls in a single useEffect — consider using useReducer or deriving state · 6 in 5 files

no-cascading-set-state

> Combine into useReducer: `const [state, dispatch] = useReducer(reducer, initialState)`

| File | Lines |
| --- | --- |
| [`packages/scan/src/web/views/notifications/slowdown-history.tsx`](https://github.com/aidenybai/react-scan/blob/78a1431a566a1ae245781386e8ba7c81dba8fe47/packages/scan/src/web/views/notifications/slowdown-history.tsx#L27) | 27, 59 |
| [`kitchen-sink/src/examples/sierpinski/index.tsx`](https://github.com/aidenybai/react-scan/blob/78a1431a566a1ae245781386e8ba7c81dba8fe47/kitchen-sink/src/examples/sierpinski/index.tsx#L27) | 27 |
| [`packages/scan/src/web/views/notifications/collapsed-event.tsx`](https://github.com/aidenybai/react-scan/blob/78a1431a566a1ae245781386e8ba7c81dba8fe47/packages/scan/src/web/views/notifications/collapsed-event.tsx#L36) | 36 |
| [`packages/scan/src/web/views/inspector/what-changed.tsx`](https://github.com/aidenybai/react-scan/blob/78a1431a566a1ae245781386e8ba7c81dba8fe47/packages/scan/src/web/views/inspector/what-changed.tsx#L36) | 36 |
| [`packages/scan/src/web/views/notifications/popover.tsx`](https://github.com/aidenybai/react-scan/blob/78a1431a566a1ae245781386e8ba7c81dba8fe47/packages/scan/src/web/views/notifications/popover.tsx#L75) | 75 |

Component "ComponentsTree" is 792 lines — consider breaking it into smaller focused components · 5 in 5 files

no-giant-component

> Extract logical sections into focused components: `<UserHeader />`, `<UserActions />`, etc.

| File | Lines |
| --- | --- |
| [`packages/scan/src/web/views/inspector/components-tree/index.tsx`](https://github.com/aidenybai/react-scan/blob/78a1431a566a1ae245781386e8ba7c81dba8fe47/packages/scan/src/web/views/inspector/components-tree/index.tsx#L386) | 386 |
| [`packages/scan/src/web/views/inspector/overlay/index.tsx`](https://github.com/aidenybai/react-scan/blob/78a1431a566a1ae245781386e8ba7c81dba8fe47/packages/scan/src/web/views/inspector/overlay/index.tsx#L48) | 48 |
| [`packages/scan/src/web/widget/resize-handle.tsx`](https://github.com/aidenybai/react-scan/blob/78a1431a566a1ae245781386e8ba7c81dba8fe47/packages/scan/src/web/widget/resize-handle.tsx#L26) | 26 |
| [`packages/scan/src/web/views/notifications/render-bar-chart.tsx`](https://github.com/aidenybai/react-scan/blob/78a1431a566a1ae245781386e8ba7c81dba8fe47/packages/scan/src/web/views/notifications/render-bar-chart.tsx#L184) | 184 |
| [`packages/scan/src/web/widget/index.tsx`](https://github.com/aidenybai/react-scan/blob/78a1431a566a1ae245781386e8ba7c81dba8fe47/packages/scan/src/web/widget/index.tsx#L44) | 44 |

"scroll" listener without { passive: true } — blocks scrolling performance. Only add { passive: true } if the handler does NOT call event.preventDefault() (passive listeners silently ignore preventDefault()) · 5 in 2 files

client-passive-event-listeners

> Add `{ passive: true }` as the third argument: `addEventListener('scroll', handler, { passive: true })`. Only do this if the handler does NOT call `event.preventDefault()` — passive listeners silently ignore `preventDefault()`, which breaks features like pull-to-refresh suppression, custom gestures, and nested-scroll containment.

| File | Lines |
| --- | --- |
| [`packages/website/public/auto.global.js`](https://github.com/aidenybai/react-scan/blob/78a1431a566a1ae245781386e8ba7c81dba8fe47/packages/website/public/auto.global.js#L2) | 2, 2, 2, 2 |
| [`packages/scan/src/new-outlines/index.ts`](https://github.com/aidenybai/react-scan/blob/78a1431a566a1ae245781386e8ba7c81dba8fe47/packages/scan/src/new-outlines/index.ts#L407) | 407 |

Enforce a clickable non-interactive element has at least one keyboard event listener. · 4 in 2 files

click-events-have-key-events

> Visible, non-interactive elements with click handlers must have one of `keyup`, `keydown`, or `keypress` listener.

| File | Lines |
| --- | --- |
| [`packages/website/app/replay/page.tsx`](https://github.com/aidenybai/react-scan/blob/78a1431a566a1ae245781386e8ba7c81dba8fe47/packages/website/app/replay/page.tsx#L136) | 136, 162, 172 |
| [`kitchen-sink/src/examples/e2e-fixture/index.tsx`](https://github.com/aidenybai/react-scan/blob/78a1431a566a1ae245781386e8ba7c81dba8fe47/kitchen-sink/src/examples/e2e-fixture/index.tsx#L46) | 46 |

Static HTML elements with event handlers require a role. · 4 in 2 files

no-static-element-interactions

> Add a role attribute to this element, or use a semantic HTML element instead.

| File | Lines |
| --- | --- |
| [`packages/website/app/replay/page.tsx`](https://github.com/aidenybai/react-scan/blob/78a1431a566a1ae245781386e8ba7c81dba8fe47/packages/website/app/replay/page.tsx#L136) | 136, 162, 172 |
| [`kitchen-sink/src/examples/e2e-fixture/index.tsx`](https://github.com/aidenybai/react-scan/blob/78a1431a566a1ae245781386e8ba7c81dba8fe47/kitchen-sink/src/examples/e2e-fixture/index.tsx#L46) | 46 |

Pure black background (bg-black) looks harsh — use a near-black tinted toward your brand hue (e.g. bg-gray-950) · 4 in 2 files

no-pure-black-background

> Tint the background slightly toward your brand hue — e.g. `#0a0a0f` or Tailwind's `bg-gray-950`. Pure black looks harsh on modern displays

| File | Lines |
| --- | --- |
| [`packages/scan/src/web/toolbar.tsx`](https://github.com/aidenybai/react-scan/blob/78a1431a566a1ae245781386e8ba7c81dba8fe47/packages/scan/src/web/toolbar.tsx#L22) | 22, 27 |
| [`packages/website/app/replay/page.tsx`](https://github.com/aidenybai/react-scan/blob/78a1431a566a1ae245781386e8ba7c81dba8fe47/packages/website/app/replay/page.tsx#L201) | 201, 222 |

aggregatedChanges.contextChanges.set is read 3 times inside this loop — hoist into a const at the top of the loop body · 4 in 2 files

js-cache-property-access

> Hoist the deep member access into a const at the top of the loop body: `const { x, y } = obj.deeply.nested`

| File | Lines |
| --- | --- |
| [`packages/scan/src/web/views/inspector/whats-changed/use-change-store.ts`](https://github.com/aidenybai/react-scan/blob/78a1431a566a1ae245781386e8ba7c81dba8fe47/packages/scan/src/web/views/inspector/whats-changed/use-change-store.ts#L123) | 123, 128 |
| [`packages/website/public/auto.global.js`](https://github.com/aidenybai/react-scan/blob/78a1431a566a1ae245781386e8ba7c81dba8fe47/packages/website/public/auto.global.js#L2) | 2, 2 |

Date.now() reachable from JSX renders differently on server vs client — wrap in useEffect+useState (client-only) or add suppressHydrationWarning to the parent if intentional · 4 in 2 files

rendering-hydration-mismatch-time

> Wrap dynamic time/random values in useEffect+useState (client-only) or add suppressHydrationWarning to the parent if intentional

| File | Lines |
| --- | --- |
| [`packages/scan/src/web/views/notifications/render-bar-chart.tsx`](https://github.com/aidenybai/react-scan/blob/78a1431a566a1ae245781386e8ba7c81dba8fe47/packages/scan/src/web/views/notifications/render-bar-chart.tsx#L261) | 261, 365 |
| [`packages/scan/src/web/views/notifications/notifications.tsx`](https://github.com/aidenybai/react-scan/blob/78a1431a566a1ae245781386e8ba7c81dba8fe47/packages/scan/src/web/views/notifications/notifications.tsx#L441) | 441, 464 |

await inside a while-loop runs the calls sequentially — for independent operations, collect them and use `await Promise.all(items.map(...))` to run them concurrently · 3 in 3 files

async-await-in-loop

> Collect the items and use `await Promise.all(items.map(...))` to run independent operations concurrently

| File | Lines |
| --- | --- |
| [`packages/scan/src/new-outlines/index.ts`](https://github.com/aidenybai/react-scan/blob/78a1431a566a1ae245781386e8ba7c81dba8fe47/packages/scan/src/new-outlines/index.ts#L153) | 153 |
| [`packages/scan/tsup.config.ts`](https://github.com/aidenybai/react-scan/blob/78a1431a566a1ae245781386e8ba7c81dba8fe47/packages/scan/tsup.config.ts#L20) | 20 |
| [`packages/website/public/auto.global.js`](https://github.com/aidenybai/react-scan/blob/78a1431a566a1ae245781386e8ba7c81dba8fe47/packages/website/public/auto.global.js#L2) | 2 |

.map().filter(Boolean) iterates twice — use .flatMap() to transform and filter in a single pass · 3 in 3 files

js-flatmap-filter

> Use `.flatMap(item => condition ? [value] : [])` — transforms and filters in a single pass instead of creating an intermediate array

| File | Lines |
| --- | --- |
| [`packages/scan/src/web/views/inspector/timeline/utils.ts`](https://github.com/aidenybai/react-scan/blob/78a1431a566a1ae245781386e8ba7c81dba8fe47/packages/scan/src/web/views/inspector/timeline/utils.ts#L148) | 148 |
| [`packages/scan/src/core/notifications/event-tracking.ts`](https://github.com/aidenybai/react-scan/blob/78a1431a566a1ae245781386e8ba7c81dba8fe47/packages/scan/src/core/notifications/event-tracking.ts#L290) | 290 |
| [`packages/website/public/auto.global.js`](https://github.com/aidenybai/react-scan/blob/78a1431a566a1ae245781386e8ba7c81dba8fe47/packages/website/public/auto.global.js#L2) | 2 |

[...array].sort() — use array.toSorted() for immutable sorting (ES2023) · 3 in 2 files

js-tosorted-immutable

> Use `array.toSorted()` (ES2023) instead of `[...array].sort()` for immutable sorting without the spread allocation

| File | Lines |
| --- | --- |
| [`packages/website/public/auto.global.js`](https://github.com/aidenybai/react-scan/blob/78a1431a566a1ae245781386e8ba7c81dba8fe47/packages/website/public/auto.global.js#L2) | 2, 2 |
| [`packages/scan/src/polyfills.ts`](https://github.com/aidenybai/react-scan/blob/78a1431a566a1ae245781386e8ba7c81dba8fe47/packages/scan/src/polyfills.ts#L4) | 4 |

Array index "index" used as key — causes bugs when list is reordered or filtered · 3 in 3 files

no-array-index-as-key

> Use a stable unique identifier: `key={item.id}` or `key={item.slug}` — index keys break on reorder/filter

| File | Lines |
| --- | --- |
| [`packages/scan/src/web/views/inspector/diff-value.tsx`](https://github.com/aidenybai/react-scan/blob/78a1431a566a1ae245781386e8ba7c81dba8fe47/packages/scan/src/web/views/inspector/diff-value.tsx#L143) | 143 |
| [`packages/scan/src/web/views/inspector/what-changed.tsx`](https://github.com/aidenybai/react-scan/blob/78a1431a566a1ae245781386e8ba7c81dba8fe47/packages/scan/src/web/views/inspector/what-changed.tsx#L452) | 452 |
| [`packages/scan/src/web/views/notifications/render-bar-chart.tsx`](https://github.com/aidenybai/react-scan/blob/78a1431a566a1ae245781386e8ba7c81dba8fe47/packages/scan/src/web/views/notifications/render-bar-chart.tsx#L502) | 502 |

setState in a "scroll" handler triggers re-renders at scroll/pointer frequency — wrap in startTransition (mark as non-urgent), use useDeferredValue, or stash in a ref + rAF throttle · 2 in 2 files

rerender-transitions-scroll

> Wrap the setState in startTransition (mark as non-urgent), use useDeferredValue, or stash in a ref + rAF throttle so scroll/pointer events don't trigger a re-render per fire

| File | Lines |
| --- | --- |
| [`packages/scan/src/new-outlines/index.ts`](https://github.com/aidenybai/react-scan/blob/78a1431a566a1ae245781386e8ba7c81dba8fe47/packages/scan/src/new-outlines/index.ts#L411) | 411 |
| [`packages/website/public/auto.global.js`](https://github.com/aidenybai/react-scan/blob/78a1431a566a1ae245781386e8ba7c81dba8fe47/packages/website/public/auto.global.js#L2) | 2 |

useState(getIsProduction()) calls initializer on every render — use useState(() => getIsProduction()) for lazy initialization · 2 in 2 files

rerender-lazy-state-init

> Wrap in an arrow function so it only runs once: `useState(() => expensiveComputation())`

| File | Lines |
| --- | --- |
| [`packages/scan/src/web/views/notifications/render-explanation.tsx`](https://github.com/aidenybai/react-scan/blob/78a1431a566a1ae245781386e8ba7c81dba8fe47/packages/scan/src/web/views/notifications/render-explanation.tsx#L16) | 16 |
| [`packages/scan/src/web/views/notifications/render-bar-chart.tsx`](https://github.com/aidenybai/react-scan/blob/78a1431a566a1ae245781386e8ba7c81dba8fe47/packages/scan/src/web/views/notifications/render-bar-chart.tsx#L67) | 67 |

Page without metadata or generateMetadata export — hurts SEO · 2 in 2 files

nextjs-missing-metadata

> Add `export const metadata = { title: '...', description: '...' }` or `export async function generateMetadata()`

| File | Lines |
| --- | --- |
| [`packages/website/app/replay/page.tsx`](https://github.com/aidenybai/react-scan/blob/78a1431a566a1ae245781386e8ba7c81dba8fe47/packages/website/app/replay/page.tsx#L1) | 1 |
| [`packages/website/app/page.tsx`](https://github.com/aidenybai/react-scan/blob/78a1431a566a1ae245781386e8ba7c81dba8fe47/packages/website/app/page.tsx#L1) | 1 |

Do not use `dangerouslySetInnerHTML` prop · 2 in 1 file

no-danger

> `dangerouslySetInnerHTML` is a way to inject HTML into your React component. This is dangerous because it can easily lead to XSS vulnerabilities.

| File | Lines |
| --- | --- |
| [`packages/website/components/install-guide.tsx`](https://github.com/aidenybai/react-scan/blob/78a1431a566a1ae245781386e8ba7c81dba8fe47/packages/website/components/install-guide.tsx#L260) | 260, 284 |

useContext is superseded by `use()` on React 19+ — `use()` reads context conditionally inside hooks, branches, and loops; switch to `import { use } from 'react'` · 1 in 1 file

no-react19-deprecated-apis

> Pass `ref` as a regular prop on function components — `forwardRef` is no longer needed in React 19+. Replace `useContext(X)` with `use(X)` for branch-aware context reads. Only enabled on projects detected as React 19+.

| File | Lines |
| --- | --- |
| [`kitchen-sink/sr

---

_Issue body truncated. See the full report at [react.review/dashboard](https://react.review/dashboard)._

Guida per i contributori

Apri la guida per i contributori

Valutazione

Questa issue non è ancora stata valutata.

Ricevi le nuove issue nella tua casella

Un breve riepilogo di issue GitHub adatte ai principianti.