@vetala/vetala 0.1.0-beta

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of @vetala/vetala might be problematic. Click here for more details.

Files changed (271) hide show
  1. package/CONTRIBUTING.md +77 -0
  2. package/LICENSE +184 -0
  3. package/README.md +136 -0
  4. package/THIRD_PARTY_LICENSES.md +17 -0
  5. package/dist/src/agent.d.ts +30 -0
  6. package/dist/src/agent.js +216 -0
  7. package/dist/src/agent.js.map +1 -0
  8. package/dist/src/approvals.d.ts +18 -0
  9. package/dist/src/approvals.js +81 -0
  10. package/dist/src/approvals.js.map +1 -0
  11. package/dist/src/cli.d.ts +2 -0
  12. package/dist/src/cli.js +87 -0
  13. package/dist/src/cli.js.map +1 -0
  14. package/dist/src/config.d.ts +12 -0
  15. package/dist/src/config.js +183 -0
  16. package/dist/src/config.js.map +1 -0
  17. package/dist/src/context-memory.d.ts +7 -0
  18. package/dist/src/context-memory.js +96 -0
  19. package/dist/src/context-memory.js.map +1 -0
  20. package/dist/src/ink/command-suggestions.d.ts +7 -0
  21. package/dist/src/ink/command-suggestions.js +179 -0
  22. package/dist/src/ink/command-suggestions.js.map +1 -0
  23. package/dist/src/ink/ink-terminal-ui.d.ts +36 -0
  24. package/dist/src/ink/ink-terminal-ui.js +79 -0
  25. package/dist/src/ink/ink-terminal-ui.js.map +1 -0
  26. package/dist/src/ink/repl-app.d.ts +9 -0
  27. package/dist/src/ink/repl-app.js +789 -0
  28. package/dist/src/ink/repl-app.js.map +1 -0
  29. package/dist/src/ink/transcript-cards.d.ts +6 -0
  30. package/dist/src/ink/transcript-cards.js +24 -0
  31. package/dist/src/ink/transcript-cards.js.map +1 -0
  32. package/dist/src/path-policy.d.ts +11 -0
  33. package/dist/src/path-policy.js +67 -0
  34. package/dist/src/path-policy.js.map +1 -0
  35. package/dist/src/process-utils.d.ts +13 -0
  36. package/dist/src/process-utils.js +52 -0
  37. package/dist/src/process-utils.js.map +1 -0
  38. package/dist/src/repl.d.ts +9 -0
  39. package/dist/src/repl.js +13 -0
  40. package/dist/src/repl.js.map +1 -0
  41. package/dist/src/sarvam/client.d.ts +15 -0
  42. package/dist/src/sarvam/client.js +208 -0
  43. package/dist/src/sarvam/client.js.map +1 -0
  44. package/dist/src/sarvam/models.d.ts +2 -0
  45. package/dist/src/sarvam/models.js +7 -0
  46. package/dist/src/sarvam/models.js.map +1 -0
  47. package/dist/src/search-provider.d.ts +6 -0
  48. package/dist/src/search-provider.js +8 -0
  49. package/dist/src/search-provider.js.map +1 -0
  50. package/dist/src/session-store.d.ts +19 -0
  51. package/dist/src/session-store.js +318 -0
  52. package/dist/src/session-store.js.map +1 -0
  53. package/dist/src/skills/runtime.d.ts +26 -0
  54. package/dist/src/skills/runtime.js +317 -0
  55. package/dist/src/skills/runtime.js.map +1 -0
  56. package/dist/src/skills/types.d.ts +25 -0
  57. package/dist/src/skills/types.js +2 -0
  58. package/dist/src/skills/types.js.map +1 -0
  59. package/dist/src/terminal-ui.d.ts +29 -0
  60. package/dist/src/terminal-ui.js +236 -0
  61. package/dist/src/terminal-ui.js.map +1 -0
  62. package/dist/src/tools/filesystem.d.ts +2 -0
  63. package/dist/src/tools/filesystem.js +622 -0
  64. package/dist/src/tools/filesystem.js.map +1 -0
  65. package/dist/src/tools/git.d.ts +2 -0
  66. package/dist/src/tools/git.js +326 -0
  67. package/dist/src/tools/git.js.map +1 -0
  68. package/dist/src/tools/index.d.ts +6 -0
  69. package/dist/src/tools/index.js +21 -0
  70. package/dist/src/tools/index.js.map +1 -0
  71. package/dist/src/tools/registry.d.ts +15 -0
  72. package/dist/src/tools/registry.js +59 -0
  73. package/dist/src/tools/registry.js.map +1 -0
  74. package/dist/src/tools/shell.d.ts +2 -0
  75. package/dist/src/tools/shell.js +97 -0
  76. package/dist/src/tools/shell.js.map +1 -0
  77. package/dist/src/tools/skill.d.ts +3 -0
  78. package/dist/src/tools/skill.js +130 -0
  79. package/dist/src/tools/skill.js.map +1 -0
  80. package/dist/src/tools/web.d.ts +3 -0
  81. package/dist/src/tools/web.js +144 -0
  82. package/dist/src/tools/web.js.map +1 -0
  83. package/dist/src/types.d.ts +236 -0
  84. package/dist/src/types.js +2 -0
  85. package/dist/src/types.js.map +1 -0
  86. package/dist/src/workspace-trust.d.ts +3 -0
  87. package/dist/src/workspace-trust.js +31 -0
  88. package/dist/src/workspace-trust.js.map +1 -0
  89. package/dist/src/xdg.d.ts +9 -0
  90. package/dist/src/xdg.js +77 -0
  91. package/dist/src/xdg.js.map +1 -0
  92. package/package.json +57 -0
  93. package/skill/agents-md-generator/SKILL.md +75 -0
  94. package/skill/agents-md-generator/references/agents_md_template.md +160 -0
  95. package/skill/agents-md-generator/references/loc_measurement.md +67 -0
  96. package/skill/agents-md-generator/references/monorepo_detection.md +78 -0
  97. package/skill/agents-md-generator/references/monorepo_strategy.md +60 -0
  98. package/skill/agents-md-generator/references/read_only_commands.md +151 -0
  99. package/skill/agents-md-generator/references/update_strategy.md +160 -0
  100. package/skill/agents-md-generator/references/working_agreements.md +53 -0
  101. package/skill/biz-opportunity-scout/SKILL.md +53 -0
  102. package/skill/biz-opportunity-scout/references/competitive_analysis.md +84 -0
  103. package/skill/biz-opportunity-scout/references/market_sizing.md +68 -0
  104. package/skill/biz-opportunity-scout/references/pmf_indicators.md +94 -0
  105. package/skill/biz-opportunity-scout/references/report_template.md +243 -0
  106. package/skill/biz-opportunity-scout/references/unit_economics.md +97 -0
  107. package/skill/code-review/SKILL.md +86 -0
  108. package/skill/code-review/references/change_analysis.md +116 -0
  109. package/skill/code-review/references/git_operations.md +115 -0
  110. package/skill/code-review/references/impact_detection.md +149 -0
  111. package/skill/code-review/references/output_format.md +137 -0
  112. package/skill/code-review/references/severity_criteria.md +100 -0
  113. package/skill/code-security-audit/SKILL.md +123 -0
  114. package/skill/code-security-audit/references/audit_process.md +277 -0
  115. package/skill/code-security-audit/references/remediation_patterns.md +599 -0
  116. package/skill/code-security-audit/references/report_format.md +391 -0
  117. package/skill/code-security-audit/references/security_domains.md +830 -0
  118. package/skill/code-security-audit/references/vulnerability_patterns.md +813 -0
  119. package/skill/composition-patterns/SKILL.md +83 -0
  120. package/skill/composition-patterns/rules/architecture-avoid-boolean-props.md +100 -0
  121. package/skill/composition-patterns/rules/architecture-compound-components.md +112 -0
  122. package/skill/composition-patterns/rules/patterns-children-over-render-props.md +87 -0
  123. package/skill/composition-patterns/rules/patterns-explicit-variants.md +100 -0
  124. package/skill/composition-patterns/rules/react19-no-forwardref.md +42 -0
  125. package/skill/composition-patterns/rules/state-context-interface.md +191 -0
  126. package/skill/composition-patterns/rules/state-decouple-implementation.md +113 -0
  127. package/skill/composition-patterns/rules/state-lift-state.md +125 -0
  128. package/skill/deploy-to-vercel/SKILL.md +293 -0
  129. package/skill/deploy-to-vercel/resources/deploy-sandbox.sh +301 -0
  130. package/skill/deploy-to-vercel/resources/deploy.sh +301 -0
  131. package/skill/doc/SKILL_GUIDELINES.md +138 -0
  132. package/skill/git-workflow/SKILL.md +94 -0
  133. package/skill/git-workflow/references/advanced-git.md +632 -0
  134. package/skill/git-workflow/references/branching-strategies.md +344 -0
  135. package/skill/git-workflow/references/ci-cd-integration.md +683 -0
  136. package/skill/git-workflow/references/code-quality-tools.md +351 -0
  137. package/skill/git-workflow/references/commit-conventions.md +439 -0
  138. package/skill/git-workflow/references/github-releases.md +288 -0
  139. package/skill/git-workflow/references/pull-request-workflow.md +773 -0
  140. package/skill/git-workflow/scripts/verify-git-workflow.sh +263 -0
  141. package/skill/jetbrains-vmoptions/SKILL.md +51 -0
  142. package/skill/jetbrains-vmoptions/references/common-options.md +357 -0
  143. package/skill/jetbrains-vmoptions/references/gc-options.md +350 -0
  144. package/skill/jetbrains-vmoptions/references/memory-options.md +339 -0
  145. package/skill/jetbrains-vmoptions/references/prerequisite-check.md +65 -0
  146. package/skill/kysely-converter/SKILL.md +62 -0
  147. package/skill/kysely-converter/references/delete.md +323 -0
  148. package/skill/kysely-converter/references/insert.md +386 -0
  149. package/skill/kysely-converter/references/operators.md +331 -0
  150. package/skill/kysely-converter/references/select.md +1000 -0
  151. package/skill/kysely-converter/references/update.md +349 -0
  152. package/skill/kysely-converter/references/window_function.md +537 -0
  153. package/skill/react-best-practices/SKILL.md +131 -0
  154. package/skill/react-best-practices/rules/advanced-event-handler-refs.md +55 -0
  155. package/skill/react-best-practices/rules/advanced-init-once.md +42 -0
  156. package/skill/react-best-practices/rules/advanced-use-latest.md +39 -0
  157. package/skill/react-best-practices/rules/async-api-routes.md +38 -0
  158. package/skill/react-best-practices/rules/async-defer-await.md +80 -0
  159. package/skill/react-best-practices/rules/async-dependencies.md +51 -0
  160. package/skill/react-best-practices/rules/async-parallel.md +28 -0
  161. package/skill/react-best-practices/rules/async-suspense-boundaries.md +99 -0
  162. package/skill/react-best-practices/rules/bundle-barrel-imports.md +59 -0
  163. package/skill/react-best-practices/rules/bundle-conditional.md +31 -0
  164. package/skill/react-best-practices/rules/bundle-defer-third-party.md +49 -0
  165. package/skill/react-best-practices/rules/bundle-dynamic-imports.md +35 -0
  166. package/skill/react-best-practices/rules/bundle-preload.md +50 -0
  167. package/skill/react-best-practices/rules/client-event-listeners.md +74 -0
  168. package/skill/react-best-practices/rules/client-localstorage-schema.md +71 -0
  169. package/skill/react-best-practices/rules/client-passive-event-listeners.md +48 -0
  170. package/skill/react-best-practices/rules/client-swr-dedup.md +56 -0
  171. package/skill/react-best-practices/rules/js-batch-dom-css.md +107 -0
  172. package/skill/react-best-practices/rules/js-cache-function-results.md +80 -0
  173. package/skill/react-best-practices/rules/js-cache-property-access.md +28 -0
  174. package/skill/react-best-practices/rules/js-cache-storage.md +70 -0
  175. package/skill/react-best-practices/rules/js-combine-iterations.md +32 -0
  176. package/skill/react-best-practices/rules/js-early-exit.md +50 -0
  177. package/skill/react-best-practices/rules/js-hoist-regexp.md +45 -0
  178. package/skill/react-best-practices/rules/js-index-maps.md +37 -0
  179. package/skill/react-best-practices/rules/js-length-check-first.md +49 -0
  180. package/skill/react-best-practices/rules/js-min-max-loop.md +82 -0
  181. package/skill/react-best-practices/rules/js-set-map-lookups.md +24 -0
  182. package/skill/react-best-practices/rules/js-tosorted-immutable.md +57 -0
  183. package/skill/react-best-practices/rules/rendering-activity.md +26 -0
  184. package/skill/react-best-practices/rules/rendering-animate-svg-wrapper.md +47 -0
  185. package/skill/react-best-practices/rules/rendering-conditional-render.md +40 -0
  186. package/skill/react-best-practices/rules/rendering-content-visibility.md +38 -0
  187. package/skill/react-best-practices/rules/rendering-hoist-jsx.md +46 -0
  188. package/skill/react-best-practices/rules/rendering-hydration-no-flicker.md +82 -0
  189. package/skill/react-best-practices/rules/rendering-hydration-suppress-warning.md +30 -0
  190. package/skill/react-best-practices/rules/rendering-svg-precision.md +28 -0
  191. package/skill/react-best-practices/rules/rendering-usetransition-loading.md +75 -0
  192. package/skill/react-best-practices/rules/rerender-defer-reads.md +39 -0
  193. package/skill/react-best-practices/rules/rerender-dependencies.md +45 -0
  194. package/skill/react-best-practices/rules/rerender-derived-state-no-effect.md +40 -0
  195. package/skill/react-best-practices/rules/rerender-derived-state.md +29 -0
  196. package/skill/react-best-practices/rules/rerender-functional-setstate.md +74 -0
  197. package/skill/react-best-practices/rules/rerender-lazy-state-init.md +58 -0
  198. package/skill/react-best-practices/rules/rerender-memo-with-default-value.md +38 -0
  199. package/skill/react-best-practices/rules/rerender-memo.md +44 -0
  200. package/skill/react-best-practices/rules/rerender-move-effect-to-event.md +45 -0
  201. package/skill/react-best-practices/rules/rerender-simple-expression-in-memo.md +35 -0
  202. package/skill/react-best-practices/rules/rerender-transitions.md +40 -0
  203. package/skill/react-best-practices/rules/rerender-use-ref-transient-values.md +73 -0
  204. package/skill/react-best-practices/rules/server-after-nonblocking.md +73 -0
  205. package/skill/react-best-practices/rules/server-auth-actions.md +96 -0
  206. package/skill/react-best-practices/rules/server-cache-lru.md +41 -0
  207. package/skill/react-best-practices/rules/server-cache-react.md +76 -0
  208. package/skill/react-best-practices/rules/server-dedup-props.md +65 -0
  209. package/skill/react-best-practices/rules/server-hoist-static-io.md +142 -0
  210. package/skill/react-best-practices/rules/server-parallel-fetching.md +83 -0
  211. package/skill/react-best-practices/rules/server-serialization.md +38 -0
  212. package/skill/react-native-skills/SKILL.md +115 -0
  213. package/skill/react-native-skills/rules/animation-derived-value.md +53 -0
  214. package/skill/react-native-skills/rules/animation-gesture-detector-press.md +95 -0
  215. package/skill/react-native-skills/rules/animation-gpu-properties.md +65 -0
  216. package/skill/react-native-skills/rules/design-system-compound-components.md +66 -0
  217. package/skill/react-native-skills/rules/fonts-config-plugin.md +71 -0
  218. package/skill/react-native-skills/rules/imports-design-system-folder.md +68 -0
  219. package/skill/react-native-skills/rules/js-hoist-intl.md +61 -0
  220. package/skill/react-native-skills/rules/list-performance-callbacks.md +44 -0
  221. package/skill/react-native-skills/rules/list-performance-function-references.md +132 -0
  222. package/skill/react-native-skills/rules/list-performance-images.md +53 -0
  223. package/skill/react-native-skills/rules/list-performance-inline-objects.md +97 -0
  224. package/skill/react-native-skills/rules/list-performance-item-expensive.md +94 -0
  225. package/skill/react-native-skills/rules/list-performance-item-memo.md +82 -0
  226. package/skill/react-native-skills/rules/list-performance-item-types.md +104 -0
  227. package/skill/react-native-skills/rules/list-performance-virtualize.md +67 -0
  228. package/skill/react-native-skills/rules/monorepo-native-deps-in-app.md +46 -0
  229. package/skill/react-native-skills/rules/monorepo-single-dependency-versions.md +63 -0
  230. package/skill/react-native-skills/rules/navigation-native-navigators.md +188 -0
  231. package/skill/react-native-skills/rules/react-compiler-destructure-functions.md +50 -0
  232. package/skill/react-native-skills/rules/react-compiler-reanimated-shared-values.md +48 -0
  233. package/skill/react-native-skills/rules/react-state-dispatcher.md +91 -0
  234. package/skill/react-native-skills/rules/react-state-fallback.md +56 -0
  235. package/skill/react-native-skills/rules/react-state-minimize.md +65 -0
  236. package/skill/react-native-skills/rules/rendering-no-falsy-and.md +74 -0
  237. package/skill/react-native-skills/rules/rendering-text-in-text-component.md +36 -0
  238. package/skill/react-native-skills/rules/scroll-position-no-state.md +82 -0
  239. package/skill/react-native-skills/rules/state-ground-truth.md +80 -0
  240. package/skill/react-native-skills/rules/ui-expo-image.md +66 -0
  241. package/skill/react-native-skills/rules/ui-image-gallery.md +104 -0
  242. package/skill/react-native-skills/rules/ui-measure-views.md +78 -0
  243. package/skill/react-native-skills/rules/ui-menus.md +174 -0
  244. package/skill/react-native-skills/rules/ui-native-modals.md +77 -0
  245. package/skill/react-native-skills/rules/ui-pressable.md +61 -0
  246. package/skill/react-native-skills/rules/ui-safe-area-scroll.md +65 -0
  247. package/skill/react-native-skills/rules/ui-scrollview-content-inset.md +45 -0
  248. package/skill/react-native-skills/rules/ui-styling.md +87 -0
  249. package/skill/react-vite-guide/SKILL.md +101 -0
  250. package/skill/react-vite-guide/references/composition-patterns.md +709 -0
  251. package/skill/react-vite-guide/references/performance-optimization.md +1222 -0
  252. package/skill/react-vite-guide/references/vite-specific.md +385 -0
  253. package/skill/react-vite-guide/references/web-interface.md +146 -0
  254. package/skill/skill-maker/SKILL.md +52 -0
  255. package/skill/skill-maker/references/content_spec.md +67 -0
  256. package/skill/skill-maker/references/frontmatter_spec.md +96 -0
  257. package/skill/skill-maker/references/input_validation.md +90 -0
  258. package/skill/skill-maker/references/skill_structure.md +74 -0
  259. package/skill/system-prompt-creator/SKILL.md +50 -0
  260. package/skill/system-prompt-creator/references/data_format_selection.md +135 -0
  261. package/skill/system-prompt-creator/references/multi_prompt_architecture.md +386 -0
  262. package/skill/system-prompt-creator/references/prompt_structure.md +140 -0
  263. package/skill/system-prompt-creator/references/quality_criteria.md +83 -0
  264. package/skill/typst-creator/SKILL.md +51 -0
  265. package/skill/typst-creator/references/layout.md +401 -0
  266. package/skill/typst-creator/references/math.md +297 -0
  267. package/skill/typst-creator/references/scripting.md +237 -0
  268. package/skill/typst-creator/references/styling.md +217 -0
  269. package/skill/typst-creator/references/syntax.md +234 -0
  270. package/skill/web-design-guidelines/SKILL.md +35 -0
  271. package/terminal.png +0 -0
@@ -0,0 +1,1222 @@
1
+ # Performance Optimization
2
+
3
+ Performance optimization patterns for React 19 + Vite SPA applications. Adapted from broader React performance guidance with Next.js-specific patterns removed and Vite equivalents provided.
4
+
5
+ ---
6
+
7
+ ## 1. Async Optimization
8
+
9
+ **Impact: CRITICAL**
10
+
11
+ Waterfalls are the #1 performance killer. Each sequential await adds full network latency.
12
+
13
+ ### 1.1 Defer Await Until Needed
14
+
15
+ **Impact: HIGH**
16
+
17
+ Deferred `await` places the keyword in the branch where the value is consumed, avoiding blocking on unused code paths.
18
+
19
+ **Incorrect: blocks both branches**
20
+
21
+ ```typescript
22
+ async function handleRequest(userId: string, skipProcessing: boolean) {
23
+ const userData = await fetchUserData(userId)
24
+
25
+ if (skipProcessing) {
26
+ return { skipped: true }
27
+ }
28
+
29
+ return processUserData(userData)
30
+ }
31
+ ```
32
+
33
+ **Correct: only blocks when needed**
34
+
35
+ ```typescript
36
+ async function handleRequest(userId: string, skipProcessing: boolean) {
37
+ if (skipProcessing) {
38
+ return { skipped: true }
39
+ }
40
+
41
+ const userData = await fetchUserData(userId)
42
+ return processUserData(userData)
43
+ }
44
+ ```
45
+
46
+ ### 1.2 Promise.all() for Independent Operations
47
+
48
+ **Impact: CRITICAL (2-10× improvement)**
49
+
50
+ Independent async operations support concurrent execution via `Promise.all()`.
51
+
52
+ **Incorrect: sequential execution, 3 round trips**
53
+
54
+ ```typescript
55
+ const user = await fetchUser()
56
+ const posts = await fetchPosts()
57
+ const comments = await fetchComments()
58
+ ```
59
+
60
+ **Correct: parallel execution, 1 round trip**
61
+
62
+ ```typescript
63
+ const [user, posts, comments] = await Promise.all([
64
+ fetchUser(),
65
+ fetchPosts(),
66
+ fetchComments()
67
+ ])
68
+ ```
69
+
70
+ ### 1.3 Dependency-Based Parallelization
71
+
72
+ **Impact: CRITICAL (2-10× improvement)**
73
+
74
+ Operations with partial dependencies support starting independent work immediately while dependent chains resolve.
75
+
76
+ **Incorrect: profile waits for config unnecessarily**
77
+
78
+ ```typescript
79
+ const [user, config] = await Promise.all([
80
+ fetchUser(),
81
+ fetchConfig()
82
+ ])
83
+ const profile = await fetchProfile(user.id)
84
+ ```
85
+
86
+ **Correct: config and profile run in parallel**
87
+
88
+ ```typescript
89
+ const userPromise = fetchUser()
90
+ const profilePromise = userPromise.then(user => fetchProfile(user.id))
91
+
92
+ const [user, config, profile] = await Promise.all([
93
+ userPromise,
94
+ fetchConfig(),
95
+ profilePromise
96
+ ])
97
+ ```
98
+
99
+ ### 1.4 Strategic Suspense Boundaries
100
+
101
+ **Impact: HIGH (faster initial paint)**
102
+
103
+ Suspense boundaries show wrapper UI while data loads. In Vite SPA, `use()` unwraps promises inside Suspense.
104
+
105
+ **Incorrect: blocks entire page**
106
+
107
+ ```tsx
108
+ function Page() {
109
+ const [data, setData] = useState(null)
110
+ const [loading, setLoading] = useState(true)
111
+
112
+ useEffect(() => {
113
+ fetchData().then(d => {
114
+ setData(d)
115
+ setLoading(false)
116
+ })
117
+ }, [])
118
+
119
+ if (loading) return <Skeleton />
120
+
121
+ return (
122
+ <div>
123
+ <Sidebar />
124
+ <Header />
125
+ <DataDisplay data={data} />
126
+ <Footer />
127
+ </div>
128
+ )
129
+ }
130
+ ```
131
+
132
+ **Correct: layout shows immediately, data streams in**
133
+
134
+ ```tsx
135
+ function Page() {
136
+ const dataPromise = useMemo(() => fetchData(), [])
137
+
138
+ return (
139
+ <div>
140
+ <Sidebar />
141
+ <Header />
142
+ <Suspense fallback={<Skeleton />}>
143
+ <DataDisplay dataPromise={dataPromise} />
144
+ </Suspense>
145
+ <Footer />
146
+ </div>
147
+ )
148
+ }
149
+
150
+ function DataDisplay({ dataPromise }: { dataPromise: Promise<Data> }) {
151
+ const data = use(dataPromise)
152
+ return <div>{data.content}</div>
153
+ }
154
+ ```
155
+
156
+ Sidebar, Header, and Footer render immediately. Only DataDisplay waits for data.
157
+
158
+ ---
159
+
160
+ ## 2. Bundle Size Optimization
161
+
162
+ **Impact: CRITICAL**
163
+
164
+ Reducing initial bundle size improves Time to Interactive and Largest Contentful Paint.
165
+
166
+ ### 2.1 Barrel File Import Cost
167
+
168
+ **Impact: CRITICAL (200-800ms import cost)**
169
+
170
+ Barrel file imports pull in the entire module tree. Direct source file imports load only what is referenced.
171
+
172
+ **Incorrect: imports entire library**
173
+
174
+ ```tsx
175
+ import { Check, X, Menu } from 'lucide-react'
176
+ // Loads 1,583 modules
177
+
178
+ import { Button, TextField } from '@mui/material'
179
+ // Loads 2,225 modules
180
+ ```
181
+
182
+ **Correct: imports only what you need**
183
+
184
+ ```tsx
185
+ import Check from 'lucide-react/dist/esm/icons/check'
186
+ import X from 'lucide-react/dist/esm/icons/x'
187
+ import Menu from 'lucide-react/dist/esm/icons/menu'
188
+
189
+ import Button from '@mui/material/Button'
190
+ import TextField from '@mui/material/TextField'
191
+ ```
192
+
193
+ Libraries commonly affected: `lucide-react`, `@mui/material`, `@mui/icons-material`, `@tabler/icons-react`, `react-icons`, `@radix-ui/react-*`, `lodash`, `date-fns`, `rxjs`.
194
+
195
+ ### 2.2 Dynamic Imports with React.lazy()
196
+
197
+ **Impact: CRITICAL (directly affects TTI and LCP)**
198
+
199
+ `React.lazy()` + `Suspense` lazy-loads large components not needed on initial render.
200
+
201
+ **Incorrect: Monaco bundles with main chunk ~300KB**
202
+
203
+ ```tsx
204
+ import { MonacoEditor } from './monaco-editor'
205
+
206
+ function CodePanel({ code }: { code: string }) {
207
+ return <MonacoEditor value={code} />
208
+ }
209
+ ```
210
+
211
+ **Correct: Monaco loads on demand**
212
+
213
+ ```tsx
214
+ import { lazy, Suspense } from 'react'
215
+
216
+ const MonacoEditor = lazy(() =>
217
+ import('./monaco-editor').then(m => ({ default: m.MonacoEditor }))
218
+ )
219
+
220
+ function CodePanel({ code }: { code: string }) {
221
+ return (
222
+ <Suspense fallback={<div>Loading editor…</div>}>
223
+ <MonacoEditor value={code} />
224
+ </Suspense>
225
+ )
226
+ }
227
+ ```
228
+
229
+ ### 2.3 Conditional Module Loading
230
+
231
+ **Impact: HIGH**
232
+
233
+ Load large data or modules only when a feature is activated.
234
+
235
+ ```tsx
236
+ function AnimationPlayer({ enabled, setEnabled }: Props) {
237
+ const [frames, setFrames] = useState<Frame[] | null>(null)
238
+
239
+ useEffect(() => {
240
+ if (enabled && !frames) {
241
+ import('./animation-frames.js')
242
+ .then(mod => setFrames(mod.frames))
243
+ .catch(() => setEnabled(false))
244
+ }
245
+ }, [enabled, frames, setEnabled])
246
+
247
+ if (!frames) return <Skeleton />
248
+ return <Canvas frames={frames} />
249
+ }
250
+ ```
251
+
252
+ ### 2.4 Defer Non-Critical Third-Party Libraries
253
+
254
+ **Impact: MEDIUM**
255
+
256
+ Analytics, logging, and error tracking are non-blocking for user interaction and support lazy loading.
257
+
258
+ **Incorrect: blocks initial bundle**
259
+
260
+ ```tsx
261
+ import { init as initAnalytics } from '@analytics/core'
262
+
263
+ function App() {
264
+ useEffect(() => {
265
+ initAnalytics({ /* config */ })
266
+ }, [])
267
+
268
+ return <Router />
269
+ }
270
+ ```
271
+
272
+ **Correct: loads after initial render**
273
+
274
+ ```tsx
275
+ function App() {
276
+ useEffect(() => {
277
+ import('@analytics/core').then(({ init }) => {
278
+ init({ /* config */ })
279
+ })
280
+ }, [])
281
+
282
+ return <Router />
283
+ }
284
+ ```
285
+
286
+ ### 2.5 Preload Based on User Intent
287
+
288
+ **Impact: MEDIUM**
289
+
290
+ Hover/focus events on triggers preload heavy bundles before navigation occurs.
291
+
292
+ ```tsx
293
+ function EditorButton({ onClick }: { onClick: () => void }) {
294
+ const preload = () => {
295
+ void import('./monaco-editor')
296
+ }
297
+
298
+ return (
299
+ <button
300
+ onMouseEnter={preload}
301
+ onFocus={preload}
302
+ onClick={onClick}
303
+ >
304
+ Open Editor
305
+ </button>
306
+ )
307
+ }
308
+ ```
309
+
310
+ ---
311
+
312
+ ## 3. Client-Side Data Fetching
313
+
314
+ **Impact: MEDIUM-HIGH**
315
+
316
+ ### 3.1 Use SWR for Automatic Deduplication
317
+
318
+ **Impact: MEDIUM-HIGH**
319
+
320
+ SWR enables request deduplication, caching, and revalidation across component instances.
321
+
322
+ **Incorrect: no deduplication, each instance fetches**
323
+
324
+ ```tsx
325
+ function UserList() {
326
+ const [users, setUsers] = useState([])
327
+ useEffect(() => {
328
+ fetch('/api/users')
329
+ .then(r => r.json())
330
+ .then(setUsers)
331
+ }, [])
332
+ }
333
+ ```
334
+
335
+ **Correct: multiple instances share one request**
336
+
337
+ ```tsx
338
+ import useSWR from 'swr'
339
+
340
+ function UserList() {
341
+ const { data: users } = useSWR('/api/users', fetcher)
342
+ }
343
+ ```
344
+
345
+ **For mutations:**
346
+
347
+ ```tsx
348
+ import useSWRMutation from 'swr/mutation'
349
+
350
+ function UpdateButton() {
351
+ const { trigger } = useSWRMutation('/api/user', updateUser)
352
+ return <button onClick={() => trigger()}>Update</button>
353
+ }
354
+ ```
355
+
356
+ ### 3.2 Deduplicate Global Event Listeners
357
+
358
+ **Impact: LOW**
359
+
360
+ `useSWRSubscription()` shares global event listeners across component instances, reducing N listeners to 1.
361
+
362
+ **Incorrect: N instances = N listeners**
363
+
364
+ ```tsx
365
+ function useKeyboardShortcut(key: string, callback: () => void) {
366
+ useEffect(() => {
367
+ const handler = (e: KeyboardEvent) => {
368
+ if (e.metaKey && e.key === key) {
369
+ callback()
370
+ }
371
+ }
372
+ window.addEventListener('keydown', handler)
373
+ return () => window.removeEventListener('keydown', handler)
374
+ }, [key, callback])
375
+ }
376
+ ```
377
+
378
+ **Correct: N instances = 1 listener**
379
+
380
+ ```tsx
381
+ import useSWRSubscription from 'swr/subscription'
382
+
383
+ const keyCallbacks = new Map<string, Set<() => void>>()
384
+
385
+ function useKeyboardShortcut(key: string, callback: () => void) {
386
+ useEffect(() => {
387
+ if (!keyCallbacks.has(key)) {
388
+ keyCallbacks.set(key, new Set())
389
+ }
390
+ keyCallbacks.get(key)!.add(callback)
391
+
392
+ return () => {
393
+ const set = keyCallbacks.get(key)
394
+ if (set) {
395
+ set.delete(callback)
396
+ if (set.size === 0) keyCallbacks.delete(key)
397
+ }
398
+ }
399
+ }, [key, callback])
400
+
401
+ useSWRSubscription('global-keydown', () => {
402
+ const handler = (e: KeyboardEvent) => {
403
+ if (e.metaKey && keyCallbacks.has(e.key)) {
404
+ keyCallbacks.get(e.key)!.forEach(cb => cb())
405
+ }
406
+ }
407
+ window.addEventListener('keydown', handler)
408
+ return () => window.removeEventListener('keydown', handler)
409
+ })
410
+ }
411
+ ```
412
+
413
+ ### 3.3 Passive Event Listeners for Scrolling
414
+
415
+ **Impact: MEDIUM**
416
+
417
+ `{ passive: true }` on touch and wheel event listeners signals the browser that `preventDefault()` is not called, enabling scroll optimization.
418
+
419
+ **Incorrect:**
420
+
421
+ ```typescript
422
+ document.addEventListener('touchstart', handleTouch)
423
+ document.addEventListener('wheel', handleWheel)
424
+ ```
425
+
426
+ **Correct:**
427
+
428
+ ```typescript
429
+ document.addEventListener('touchstart', handleTouch, { passive: true })
430
+ document.addEventListener('wheel', handleWheel, { passive: true })
431
+ ```
432
+
433
+ Use passive when: tracking/analytics, logging, any listener that doesn't call `preventDefault()`.
434
+
435
+ ### 3.4 Version and Minimize localStorage Data
436
+
437
+ **Impact: MEDIUM**
438
+
439
+ Add version prefix to keys and store only needed fields.
440
+
441
+ ```typescript
442
+ const VERSION = 'v2'
443
+
444
+ function saveConfig(config: { theme: string; language: string }) {
445
+ try {
446
+ localStorage.setItem(`userConfig:${VERSION}`, JSON.stringify(config))
447
+ } catch {
448
+ // Throws in incognito, quota exceeded, or disabled
449
+ }
450
+ }
451
+
452
+ function loadConfig() {
453
+ try {
454
+ const data = localStorage.getItem(`userConfig:${VERSION}`)
455
+ return data ? JSON.parse(data) : null
456
+ } catch {
457
+ return null
458
+ }
459
+ }
460
+ ```
461
+
462
+ Always wrap in try-catch. `getItem()` and `setItem()` throw in incognito/private browsing.
463
+
464
+ ---
465
+
466
+ ## 4. Re-render Optimization
467
+
468
+ **Impact: MEDIUM**
469
+
470
+ ### 4.1 Calculate Derived State During Rendering
471
+
472
+ **Impact: MEDIUM**
473
+
474
+ Values computable from current props/state are derived during render, eliminating redundant state and effects.
475
+
476
+ **Incorrect: redundant state and effect**
477
+
478
+ ```tsx
479
+ function Form() {
480
+ const [firstName, setFirstName] = useState('First')
481
+ const [lastName, setLastName] = useState('Last')
482
+ const [fullName, setFullName] = useState('')
483
+
484
+ useEffect(() => {
485
+ setFullName(firstName + ' ' + lastName)
486
+ }, [firstName, lastName])
487
+
488
+ return <p>{fullName}</p>
489
+ }
490
+ ```
491
+
492
+ **Correct: derive during render**
493
+
494
+ ```tsx
495
+ function Form() {
496
+ const [firstName, setFirstName] = useState('First')
497
+ const [lastName, setLastName] = useState('Last')
498
+ const fullName = firstName + ' ' + lastName
499
+
500
+ return <p>{fullName}</p>
501
+ }
502
+ ```
503
+
504
+ ### 4.2 Defer State Reads to Usage Point
505
+
506
+ **Impact: MEDIUM**
507
+
508
+ Reading dynamic state only inside callbacks avoids subscribing to changes that do not affect render output.
509
+
510
+ **Incorrect: subscribes to all URL changes**
511
+
512
+ ```tsx
513
+ function ShareButton({ chatId }: { chatId: string }) {
514
+ const [searchParams] = useSearchParams()
515
+
516
+ const handleShare = () => {
517
+ const ref = searchParams.get('ref')
518
+ shareChat(chatId, { ref })
519
+ }
520
+
521
+ return <button onClick={handleShare}>Share</button>
522
+ }
523
+ ```
524
+
525
+ **Correct: reads on demand**
526
+
527
+ ```tsx
528
+ function ShareButton({ chatId }: { chatId: string }) {
529
+ const handleShare = () => {
530
+ const params = new URLSearchParams(window.location.search)
531
+ const ref = params.get('ref')
532
+ shareChat(chatId, { ref })
533
+ }
534
+
535
+ return <button onClick={handleShare}>Share</button>
536
+ }
537
+ ```
538
+
539
+ ### 4.3 Simple Expressions vs useMemo
540
+
541
+ **Impact: LOW-MEDIUM**
542
+
543
+ For simple expressions with primitive results, `useMemo` overhead exceeds the computation cost.
544
+
545
+ **Incorrect:**
546
+
547
+ ```tsx
548
+ const isLoading = useMemo(() => {
549
+ return user.isLoading || notifications.isLoading
550
+ }, [user.isLoading, notifications.isLoading])
551
+ ```
552
+
553
+ **Correct:**
554
+
555
+ ```tsx
556
+ const isLoading = user.isLoading || notifications.isLoading
557
+ ```
558
+
559
+ ### 4.4 Extract Default Non-primitive Values from Memoized Components
560
+
561
+ **Impact: MEDIUM**
562
+
563
+ Default non-primitive parameter values create new instances on every render, breaking `memo()`.
564
+
565
+ **Incorrect:**
566
+
567
+ ```tsx
568
+ const UserAvatar = memo(function UserAvatar({ onClick = () => {} }: Props) {
569
+ // ...
570
+ })
571
+ ```
572
+
573
+ **Correct:**
574
+
575
+ ```tsx
576
+ const NOOP = () => {};
577
+
578
+ const UserAvatar = memo(function UserAvatar({ onClick = NOOP }: Props) {
579
+ // ...
580
+ })
581
+ ```
582
+
583
+ ### 4.5 Extract to Memoized Components
584
+
585
+ **Impact: MEDIUM**
586
+
587
+ Memoized components encapsulate expensive work, enabling early returns that skip computation entirely.
588
+
589
+ **Incorrect: computes avatar even when loading**
590
+
591
+ ```tsx
592
+ function Profile({ user, loading }: Props) {
593
+ const avatar = useMemo(() => {
594
+ const id = computeAvatarId(user)
595
+ return <Avatar id={id} />
596
+ }, [user])
597
+
598
+ if (loading) return <Skeleton />
599
+ return <div>{avatar}</div>
600
+ }
601
+ ```
602
+
603
+ **Correct: skips computation when loading**
604
+
605
+ ```tsx
606
+ const UserAvatar = memo(function UserAvatar({ user }: { user: User }) {
607
+ const id = useMemo(() => computeAvatarId(user), [user])
608
+ return <Avatar id={id} />
609
+ })
610
+
611
+ function Profile({ user, loading }: Props) {
612
+ if (loading) return <Skeleton />
613
+ return (
614
+ <div>
615
+ <UserAvatar user={user} />
616
+ </div>
617
+ )
618
+ }
619
+ ```
620
+
621
+ > If React Compiler is enabled, manual `memo()` and `useMemo()` are unnecessary.
622
+
623
+ ### 4.6 Narrow Effect Dependencies
624
+
625
+ **Impact: LOW**
626
+
627
+ Primitive dependencies trigger effects only on meaningful changes; object dependencies trigger on every new reference.
628
+
629
+ ```tsx
630
+ // ❌ Re-runs on any user field change
631
+ useEffect(() => {
632
+ console.log(user.id)
633
+ }, [user])
634
+
635
+ // ✅ Re-runs only when id changes
636
+ useEffect(() => {
637
+ console.log(user.id)
638
+ }, [user.id])
639
+ ```
640
+
641
+ **Derived state outside effect:**
642
+
643
+ ```tsx
644
+ // ❌ Runs on width=767, 766, 765...
645
+ useEffect(() => {
646
+ if (width < 768) enableMobileMode()
647
+ }, [width])
648
+
649
+ // ✅ Runs only on boolean transition
650
+ const isMobile = width < 768
651
+ useEffect(() => {
652
+ if (isMobile) enableMobileMode()
653
+ }, [isMobile])
654
+ ```
655
+
656
+ ### 4.7 Interaction Logic in Event Handlers
657
+
658
+ **Impact: MEDIUM**
659
+
660
+ Side effects triggered by specific user actions belong in event handlers. Modeling actions as state + effect creates unnecessary render cycles.
661
+
662
+ **Incorrect:**
663
+
664
+ ```tsx
665
+ function Form() {
666
+ const [submitted, setSubmitted] = useState(false)
667
+ const theme = useContext(ThemeContext)
668
+
669
+ useEffect(() => {
670
+ if (submitted) {
671
+ post('/api/register')
672
+ showToast('Registered', theme)
673
+ }
674
+ }, [submitted, theme])
675
+
676
+ return <button onClick={() => setSubmitted(true)}>Submit</button>
677
+ }
678
+ ```
679
+
680
+ **Correct:**
681
+
682
+ ```tsx
683
+ function Form() {
684
+ const theme = useContext(ThemeContext)
685
+
686
+ function handleSubmit() {
687
+ post('/api/register')
688
+ showToast('Registered', theme)
689
+ }
690
+
691
+ return <button onClick={handleSubmit}>Submit</button>
692
+ }
693
+ ```
694
+
695
+ ### 4.8 Subscribe to Derived State
696
+
697
+ **Impact: MEDIUM**
698
+
699
+ Derived boolean state changes less frequently than continuous values, reducing re-render frequency.
700
+
701
+ **Incorrect: re-renders on every pixel change**
702
+
703
+ ```tsx
704
+ function Sidebar() {
705
+ const width = useWindowWidth()
706
+ const isMobile = width < 768
707
+ return <nav className={isMobile ? 'mobile' : 'desktop'} />
708
+ }
709
+ ```
710
+
711
+ **Correct: re-renders only when boolean changes**
712
+
713
+ ```tsx
714
+ function Sidebar() {
715
+ const isMobile = useMediaQuery('(max-width: 767px)')
716
+ return <nav className={isMobile ? 'mobile' : 'desktop'} />
717
+ }
718
+ ```
719
+
720
+ ### 4.9 Use Functional setState Updates
721
+
722
+ **Impact: MEDIUM**
723
+
724
+ Prevents stale closures and unnecessary callback recreations.
725
+
726
+ **Incorrect: requires state as dependency**
727
+
728
+ ```tsx
729
+ const addItems = useCallback((newItems: Item[]) => {
730
+ setItems([...items, ...newItems])
731
+ }, [items]) // items dependency causes recreations
732
+ ```
733
+
734
+ **Correct: stable callbacks, no stale closures**
735
+
736
+ ```tsx
737
+ const addItems = useCallback((newItems: Item[]) => {
738
+ setItems(curr => [...curr, ...newItems])
739
+ }, []) // No dependencies needed
740
+ ```
741
+
742
+ **When to use functional updates:** any `setState` that depends on the current state value, inside `useCallback`/`useMemo`, event handlers that reference state, async operations.
743
+
744
+ **When direct updates are fine:** setting to a static value (`setCount(0)`), setting from props/arguments only, state doesn't depend on previous value.
745
+
746
+ ### 4.10 Lazy State Initialization
747
+
748
+ **Impact: MEDIUM**
749
+
750
+ A function passed to `useState` runs only on initial render; a direct expression runs on every render.
751
+
752
+ **Incorrect: runs on every render**
753
+
754
+ ```tsx
755
+ const [settings, setSettings] = useState(
756
+ JSON.parse(localStorage.getItem('settings') || '{}')
757
+ )
758
+ ```
759
+
760
+ **Correct: runs only once**
761
+
762
+ ```tsx
763
+ const [settings, setSettings] = useState(() => {
764
+ const stored = localStorage.getItem('settings')
765
+ return stored ? JSON.parse(stored) : {}
766
+ })
767
+ ```
768
+
769
+ ### 4.11 Use Transitions for Non-Urgent Updates
770
+
771
+ **Impact: MEDIUM**
772
+
773
+ ```tsx
774
+ import { startTransition } from 'react'
775
+
776
+ function ScrollTracker() {
777
+ const [scrollY, setScrollY] = useState(0)
778
+ useEffect(() => {
779
+ const handler = () => {
780
+ startTransition(() => setScrollY(window.scrollY))
781
+ }
782
+ window.addEventListener('scroll', handler, { passive: true })
783
+ return () => window.removeEventListener('scroll', handler)
784
+ }, [])
785
+ }
786
+ ```
787
+
788
+ ### 4.12 Use useRef for Transient Values
789
+
790
+ **Impact: MEDIUM**
791
+
792
+ When a value changes frequently and you don't need a re-render on every update, use `useRef` instead of `useState`.
793
+
794
+ **Incorrect: renders every update**
795
+
796
+ ```tsx
797
+ function Tracker() {
798
+ const [lastX, setLastX] = useState(0)
799
+
800
+ useEffect(() => {
801
+ const onMove = (e: MouseEvent) => setLastX(e.clientX)
802
+ window.addEventListener('mousemove', onMove)
803
+ return () => window.removeEventListener('mousemove', onMove)
804
+ }, [])
805
+
806
+ return <div style={{ left: lastX }} />
807
+ }
808
+ ```
809
+
810
+ **Correct: no re-render for tracking**
811
+
812
+ ```tsx
813
+ function Tracker() {
814
+ const lastXRef = useRef(0)
815
+ const dotRef = useRef<HTMLDivElement>(null)
816
+
817
+ useEffect(() => {
818
+ const onMove = (e: MouseEvent) => {
819
+ lastXRef.current = e.clientX
820
+ if (dotRef.current) {
821
+ dotRef.current.style.transform = `translateX(${e.clientX}px)`
822
+ }
823
+ }
824
+ window.addEventListener('mousemove', onMove)
825
+ return () => window.removeEventListener('mousemove', onMove)
826
+ }, [])
827
+
828
+ return <div ref={dotRef} style={{ transform: 'translateX(0px)' }} />
829
+ }
830
+ ```
831
+
832
+ ---
833
+
834
+ ## 5. Rendering Performance
835
+
836
+ **Impact: MEDIUM**
837
+
838
+ ### 5.1 Animate SVG Wrapper Instead of SVG Element
839
+
840
+ **Impact: LOW**
841
+
842
+ ```tsx
843
+ // ❌ No hardware acceleration
844
+ <svg className="animate-spin" width="24" height="24" viewBox="0 0 24 24">
845
+ <circle cx="12" cy="12" r="10" stroke="currentColor" />
846
+ </svg>
847
+
848
+ // ✅ Hardware accelerated
849
+ <div className="animate-spin">
850
+ <svg width="24" height="24" viewBox="0 0 24 24">
851
+ <circle cx="12" cy="12" r="10" stroke="currentColor" />
852
+ </svg>
853
+ </div>
854
+ ```
855
+
856
+ ### 5.2 CSS content-visibility for Long Lists
857
+
858
+ **Impact: HIGH**
859
+
860
+ ```css
861
+ .message-item {
862
+ content-visibility: auto;
863
+ contain-intrinsic-size: 0 80px;
864
+ }
865
+ ```
866
+
867
+ For 1000 items, browser skips layout/paint for ~990 off-screen items.
868
+
869
+ ### 5.3 Hoist Static JSX Elements
870
+
871
+ **Impact: LOW**
872
+
873
+ ```tsx
874
+ // ❌ Recreates element every render
875
+ function LoadingSkeleton() {
876
+ return <div className="animate-pulse h-20 bg-gray-200" />
877
+ }
878
+
879
+ // ✅ Reuses same element
880
+ const loadingSkeleton = (
881
+ <div className="animate-pulse h-20 bg-gray-200" />
882
+ )
883
+ ```
884
+
885
+ > If React Compiler is enabled, it automatically hoists static JSX.
886
+
887
+ ### 5.4 Use Activity Component for Show/Hide
888
+
889
+ **Impact: MEDIUM**
890
+
891
+ ```tsx
892
+ import { Activity } from 'react'
893
+
894
+ function Dropdown({ isOpen }: Props) {
895
+ return (
896
+ <Activity mode={isOpen ? 'visible' : 'hidden'}>
897
+ <ExpensiveMenu />
898
+ </Activity>
899
+ )
900
+ }
901
+ ```
902
+
903
+ Avoids expensive re-renders and state loss.
904
+
905
+ ### 5.5 Use Explicit Conditional Rendering
906
+
907
+ **Impact: LOW**
908
+
909
+ ```tsx
910
+ // ❌ Renders "0" when count is 0
911
+ {count && <span className="badge">{count}</span>}
912
+
913
+ // ✅ Renders nothing when count is 0
914
+ {count > 0 ? <span className="badge">{count}</span> : null}
915
+ ```
916
+
917
+ ### 5.6 Use useTransition Over Manual Loading States
918
+
919
+ **Impact: LOW**
920
+
921
+ ```tsx
922
+ import { useTransition, useState } from 'react'
923
+
924
+ function SearchResults() {
925
+ const [query, setQuery] = useState('')
926
+ const [results, setResults] = useState([])
927
+ const [isPending, startTransition] = useTransition()
928
+
929
+ const handleSearch = (value: string) => {
930
+ setQuery(value)
931
+ startTransition(async () => {
932
+ const data = await fetchResults(value)
933
+ setResults(data)
934
+ })
935
+ }
936
+
937
+ return (
938
+ <>
939
+ <input onChange={(e) => handleSearch(e.target.value)} />
940
+ {isPending && <Spinner />}
941
+ <ResultsList results={results} />
942
+ </>
943
+ )
944
+ }
945
+ ```
946
+
947
+ ---
948
+
949
+ ## 6. JavaScript Performance
950
+
951
+ **Impact: LOW-MEDIUM**
952
+
953
+ ### 6.1 Layout Thrashing
954
+
955
+ **Impact: MEDIUM**
956
+
957
+ Interleaving style writes with layout reads forces synchronous reflow on each read. Batching writes then reading once avoids this.
958
+
959
+ ```typescript
960
+ // ❌ Forces reflow on each read
961
+ element.style.width = '100px'
962
+ const width = element.offsetWidth // Forces reflow
963
+ element.style.height = '200px'
964
+ const height = element.offsetHeight // Forces another reflow
965
+
966
+ // ✅ Batch writes, then read once
967
+ element.style.width = '100px'
968
+ element.style.height = '200px'
969
+ const { width, height } = element.getBoundingClientRect()
970
+ ```
971
+
972
+ Prefer CSS classes over inline styles when possible.
973
+
974
+ ### 6.2 Build Index Maps for Repeated Lookups
975
+
976
+ **Impact: LOW-MEDIUM**
977
+
978
+ ```typescript
979
+ // ❌ O(n) per lookup
980
+ orders.map(order => ({
981
+ ...order,
982
+ user: users.find(u => u.id === order.userId)
983
+ }))
984
+
985
+ // ✅ O(1) per lookup
986
+ const userById = new Map(users.map(u => [u.id, u]))
987
+ orders.map(order => ({
988
+ ...order,
989
+ user: userById.get(order.userId)
990
+ }))
991
+ ```
992
+
993
+ ### 6.3 Cache Repeated Function Calls
994
+
995
+ **Impact: MEDIUM**
996
+
997
+ ```typescript
998
+ const slugifyCache = new Map<string, string>()
999
+
1000
+ function cachedSlugify(text: string): string {
1001
+ if (slugifyCache.has(text)) return slugifyCache.get(text)!
1002
+ const result = slugify(text)
1003
+ slugifyCache.set(text, result)
1004
+ return result
1005
+ }
1006
+ ```
1007
+
1008
+ ### 6.4 Cache Storage API Calls
1009
+
1010
+ **Impact: LOW-MEDIUM**
1011
+
1012
+ ```typescript
1013
+ const storageCache = new Map<string, string | null>()
1014
+
1015
+ function getLocalStorage(key: string) {
1016
+ if (!storageCache.has(key)) {
1017
+ storageCache.set(key, localStorage.getItem(key))
1018
+ }
1019
+ return storageCache.get(key)
1020
+ }
1021
+
1022
+ function setLocalStorage(key: string, value: string) {
1023
+ localStorage.setItem(key, value)
1024
+ storageCache.set(key, value)
1025
+ }
1026
+ ```
1027
+
1028
+ Invalidate on external changes:
1029
+
1030
+ ```typescript
1031
+ window.addEventListener('storage', (e) => {
1032
+ if (e.key) storageCache.delete(e.key)
1033
+ })
1034
+ ```
1035
+
1036
+ ### 6.5 Combined Array Iterations
1037
+
1038
+ **Impact: LOW-MEDIUM**
1039
+
1040
+ ```typescript
1041
+ // ❌ 3 iterations
1042
+ const admins = users.filter(u => u.isAdmin)
1043
+ const testers = users.filter(u => u.isTester)
1044
+ const inactive = users.filter(u => !u.isActive)
1045
+
1046
+ // ✅ 1 iteration
1047
+ const admins: User[] = []
1048
+ const testers: User[] = []
1049
+ const inactive: User[] = []
1050
+
1051
+ for (const user of users) {
1052
+ if (user.isAdmin) admins.push(user)
1053
+ if (user.isTester) testers.push(user)
1054
+ if (!user.isActive) inactive.push(user)
1055
+ }
1056
+ ```
1057
+
1058
+ ### 6.6 Use Set/Map for O(1) Lookups
1059
+
1060
+ **Impact: LOW-MEDIUM**
1061
+
1062
+ ```typescript
1063
+ // ❌ O(n) per check
1064
+ const allowedIds = ['a', 'b', 'c']
1065
+ items.filter(item => allowedIds.includes(item.id))
1066
+
1067
+ // ✅ O(1) per check
1068
+ const allowedIds = new Set(['a', 'b', 'c'])
1069
+ items.filter(item => allowedIds.has(item.id))
1070
+ ```
1071
+
1072
+ ### 6.7 Use toSorted() for Immutability
1073
+
1074
+ **Impact: MEDIUM-HIGH**
1075
+
1076
+ ```typescript
1077
+ // ❌ Mutates the users prop array
1078
+ const sorted = users.sort((a, b) => a.name.localeCompare(b.name))
1079
+
1080
+ // ✅ Creates new sorted array
1081
+ const sorted = users.toSorted((a, b) => a.name.localeCompare(b.name))
1082
+ ```
1083
+
1084
+ Other immutable methods: `.toReversed()`, `.toSpliced()`, `.with()`.
1085
+
1086
+ ### 6.8 Early Return from Functions
1087
+
1088
+ **Impact: LOW-MEDIUM**
1089
+
1090
+ ```typescript
1091
+ // ❌ Processes all items even after finding answer
1092
+ function validateUsers(users: User[]) {
1093
+ let hasError = false
1094
+ let errorMessage = ''
1095
+ for (const user of users) {
1096
+ if (!user.email) { hasError = true; errorMessage = 'Email required' }
1097
+ if (!user.name) { hasError = true; errorMessage = 'Name required' }
1098
+ }
1099
+ return hasError ? { valid: false, error: errorMessage } : { valid: true }
1100
+ }
1101
+
1102
+ // ✅ Returns immediately on first error
1103
+ function validateUsers(users: User[]) {
1104
+ for (const user of users) {
1105
+ if (!user.email) return { valid: false, error: 'Email required' }
1106
+ if (!user.name) return { valid: false, error: 'Name required' }
1107
+ }
1108
+ return { valid: true }
1109
+ }
1110
+ ```
1111
+
1112
+ ### 6.9 Hoisted RegExp Creation
1113
+
1114
+ **Impact: LOW-MEDIUM**
1115
+
1116
+ ```tsx
1117
+ // ❌ New RegExp every render
1118
+ function Highlighter({ text, query }: Props) {
1119
+ const regex = new RegExp(`(${query})`, 'gi')
1120
+ const parts = text.split(regex)
1121
+ return <>{parts.map((part, i) => ...)}</>
1122
+ }
1123
+
1124
+ // ✅ Memoize
1125
+ function Highlighter({ text, query }: Props) {
1126
+ const regex = useMemo(
1127
+ () => new RegExp(`(${escapeRegex(query)})`, 'gi'),
1128
+ [query]
1129
+ )
1130
+ const parts = text.split(regex)
1131
+ return <>{parts.map((part, i) => ...)}</>
1132
+ }
1133
+ ```
1134
+
1135
+ ### 6.10 Early Length Check for Array Comparisons
1136
+
1137
+ **Impact: MEDIUM-HIGH**
1138
+
1139
+ ```typescript
1140
+ function hasChanges(current: string[], original: string[]) {
1141
+ if (current.length !== original.length) return true
1142
+
1143
+ const currentSorted = current.toSorted()
1144
+ const originalSorted = original.toSorted()
1145
+ for (let i = 0; i < currentSorted.length; i++) {
1146
+ if (currentSorted[i] !== originalSorted[i]) return true
1147
+ }
1148
+ return false
1149
+ }
1150
+ ```
1151
+
1152
+ ---
1153
+
1154
+ ## 7. Advanced Patterns
1155
+
1156
+ **Impact: LOW**
1157
+
1158
+ ### 7.1 Initialize App Once, Not Per Mount
1159
+
1160
+ **Impact: LOW-MEDIUM**
1161
+
1162
+ ```tsx
1163
+ let didInit = false
1164
+
1165
+ function App() {
1166
+ useEffect(() => {
1167
+ if (didInit) return
1168
+ didInit = true
1169
+ loadFromStorage()
1170
+ checkAuthToken()
1171
+ }, [])
1172
+
1173
+ // ...
1174
+ }
1175
+ ```
1176
+
1177
+ ### 7.2 Store Event Handlers in Refs
1178
+
1179
+ **Impact: LOW**
1180
+
1181
+ ```tsx
1182
+ import { useEffectEvent } from 'react'
1183
+
1184
+ function useWindowEvent(event: string, handler: (e: Event) => void) {
1185
+ const onEvent = useEffectEvent(handler)
1186
+
1187
+ useEffect(() => {
1188
+ window.addEventListener(event, onEvent)
1189
+ return () => window.removeEventListener(event, onEvent)
1190
+ }, [event])
1191
+ }
1192
+ ```
1193
+
1194
+ ### 7.3 useEffectEvent for Stable Callback Refs
1195
+
1196
+ **Impact: LOW**
1197
+
1198
+ ```tsx
1199
+ import { useEffectEvent } from 'react'
1200
+
1201
+ function SearchInput({ onSearch }: { onSearch: (q: string) => void }) {
1202
+ const [query, setQuery] = useState('')
1203
+ const onSearchEvent = useEffectEvent(onSearch)
1204
+
1205
+ useEffect(() => {
1206
+ const timeout = setTimeout(() => onSearchEvent(query), 300)
1207
+ return () => clearTimeout(timeout)
1208
+ }, [query])
1209
+ }
1210
+ ```
1211
+
1212
+ ---
1213
+
1214
+ ## References
1215
+
1216
+ 1. [https://react.dev](https://react.dev)
1217
+ 2. [https://react.dev/reference/react/use](https://react.dev/reference/react/use)
1218
+ 3. [https://react.dev/learn/you-might-not-need-an-effect](https://react.dev/learn/you-might-not-need-an-effect)
1219
+ 4. [https://react.dev/reference/react/useTransition](https://react.dev/reference/react/useTransition)
1220
+ 5. [https://swr.vercel.app](https://swr.vercel.app)
1221
+ 6. [https://github.com/shuding/better-all](https://github.com/shuding/better-all)
1222
+ 7. [https://vercel.com/blog/how-we-optimized-package-imports-in-next-js](https://vercel.com/blog/how-we-optimized-package-imports-in-next-js)