@brookmind/ai-toolkit 1.0.5 → 1.1.2

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.
Files changed (152) hide show
  1. package/README.md +60 -14
  2. package/agents/code-reviewer.md +6 -1
  3. package/agents/code-simplifier.md +52 -0
  4. package/dist/index.d.ts.map +1 -1
  5. package/dist/index.js +257 -220
  6. package/dist/index.js.map +1 -1
  7. package/mcps/context7/.mcp.json +13 -0
  8. package/mcps/expo-mcp/.mcp.json +13 -0
  9. package/mcps/figma-mcp/.mcp.json +4 -6
  10. package/package.json +4 -4
  11. package/skills/pdf-processing-pro/FORMS.md +610 -0
  12. package/skills/pdf-processing-pro/OCR.md +137 -0
  13. package/skills/pdf-processing-pro/SKILL.md +296 -0
  14. package/skills/pdf-processing-pro/TABLES.md +626 -0
  15. package/skills/pdf-processing-pro/scripts/analyze_form.py +307 -0
  16. package/skills/react-best-practices/AGENTS.md +915 -0
  17. package/skills/react-best-practices/README.md +127 -0
  18. package/skills/react-best-practices/SKILL.md +110 -0
  19. package/skills/react-best-practices/metadata.json +14 -0
  20. package/skills/react-best-practices/rules/_sections.md +41 -0
  21. package/skills/react-best-practices/rules/_template.md +28 -0
  22. package/skills/react-best-practices/rules/advanced-event-handler-refs.md +80 -0
  23. package/skills/react-best-practices/rules/advanced-use-latest.md +76 -0
  24. package/skills/react-best-practices/rules/async-defer-await.md +80 -0
  25. package/skills/react-best-practices/rules/async-dependencies.md +36 -0
  26. package/skills/react-best-practices/rules/async-parallel.md +28 -0
  27. package/skills/react-best-practices/rules/async-suspense-boundaries.md +100 -0
  28. package/skills/react-best-practices/rules/bundle-barrel-imports.md +42 -0
  29. package/skills/react-best-practices/rules/bundle-conditional.md +106 -0
  30. package/skills/react-best-practices/rules/bundle-preload.md +44 -0
  31. package/skills/react-best-practices/rules/client-event-listeners.md +131 -0
  32. package/skills/react-best-practices/rules/client-swr-dedup.md +133 -0
  33. package/skills/react-best-practices/rules/js-batch-dom-css.md +82 -0
  34. package/skills/react-best-practices/rules/js-cache-function-results.md +80 -0
  35. package/skills/react-best-practices/rules/js-cache-property-access.md +28 -0
  36. package/skills/react-best-practices/rules/js-cache-storage.md +70 -0
  37. package/skills/react-best-practices/rules/js-combine-iterations.md +32 -0
  38. package/skills/react-best-practices/rules/js-early-exit.md +50 -0
  39. package/skills/react-best-practices/rules/js-hoist-regexp.md +45 -0
  40. package/skills/react-best-practices/rules/js-index-maps.md +37 -0
  41. package/skills/react-best-practices/rules/js-length-check-first.md +49 -0
  42. package/skills/react-best-practices/rules/js-min-max-loop.md +82 -0
  43. package/skills/react-best-practices/rules/js-set-map-lookups.md +24 -0
  44. package/skills/react-best-practices/rules/js-tosorted-immutable.md +57 -0
  45. package/skills/react-best-practices/rules/rendering-activity.md +90 -0
  46. package/skills/react-best-practices/rules/rendering-animate-svg-wrapper.md +47 -0
  47. package/skills/react-best-practices/rules/rendering-conditional-render.md +40 -0
  48. package/skills/react-best-practices/rules/rendering-content-visibility.md +38 -0
  49. package/skills/react-best-practices/rules/rendering-hoist-jsx.md +65 -0
  50. package/skills/react-best-practices/rules/rendering-svg-precision.md +28 -0
  51. package/skills/react-best-practices/rules/rerender-defer-reads.md +39 -0
  52. package/skills/react-best-practices/rules/rerender-dependencies.md +45 -0
  53. package/skills/react-best-practices/rules/rerender-derived-state.md +29 -0
  54. package/skills/react-best-practices/rules/rerender-functional-setstate.md +74 -0
  55. package/skills/react-best-practices/rules/rerender-lazy-state-init.md +58 -0
  56. package/skills/react-best-practices/rules/rerender-memo.md +85 -0
  57. package/skills/react-best-practices/rules/rerender-transitions.md +40 -0
  58. package/themes/README.md +68 -0
  59. package/themes/claude-vivid.json +72 -0
  60. package/mcps/context7/.claude-plugin +0 -1
  61. package/mcps/context7/README.md +0 -1
  62. package/mcps/context7/server.json +0 -1
  63. package/mcps/expo-mcp/README.md +0 -33
  64. package/mcps/expo-mcp/package.json +0 -30
  65. package/mcps/figma-mcp/README.md +0 -554
  66. package/mcps/figma-mcp/server.json +0 -17
  67. package/mcps/figma-mcp/skills/code-connect-components +0 -1
  68. package/mcps/figma-mcp/skills/create-design-system-rules +0 -1
  69. package/mcps/figma-mcp/skills/implement-design +0 -1
  70. package/mcps/pg-aiguide/.claude-plugin +0 -1
  71. package/mcps/pg-aiguide/CLAUDE.md +0 -21
  72. package/mcps/pg-aiguide/README.md +0 -275
  73. package/mcps/pg-aiguide/skills/design-postgres-tables +0 -1
  74. package/mcps/pg-aiguide/skills/find-hypertable-candidates +0 -1
  75. package/mcps/pg-aiguide/skills/migrate-postgres-tables-to-hypertables +0 -1
  76. package/mcps/pg-aiguide/skills/setup-timescaledb-hypertables +0 -1
  77. package/mcps/pg-aiguide/skills.yaml +0 -4
  78. package/skills/cloudflare-cli/SKILL.md +0 -151
  79. package/skills/docx/LICENSE.txt +0 -30
  80. package/skills/docx/SKILL.md +0 -197
  81. package/skills/docx/docx-js.md +0 -350
  82. package/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/dml-chart.xsd +0 -1499
  83. package/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/dml-chartDrawing.xsd +0 -146
  84. package/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/dml-diagram.xsd +0 -1085
  85. package/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/dml-lockedCanvas.xsd +0 -11
  86. package/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/dml-main.xsd +0 -3081
  87. package/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/dml-picture.xsd +0 -23
  88. package/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/dml-spreadsheetDrawing.xsd +0 -185
  89. package/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/dml-wordprocessingDrawing.xsd +0 -287
  90. package/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/pml.xsd +0 -1676
  91. package/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/shared-additionalCharacteristics.xsd +0 -28
  92. package/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/shared-bibliography.xsd +0 -144
  93. package/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/shared-commonSimpleTypes.xsd +0 -174
  94. package/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/shared-customXmlDataProperties.xsd +0 -25
  95. package/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/shared-customXmlSchemaProperties.xsd +0 -18
  96. package/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesCustom.xsd +0 -59
  97. package/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesExtended.xsd +0 -56
  98. package/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesVariantTypes.xsd +0 -195
  99. package/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/shared-math.xsd +0 -582
  100. package/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/shared-relationshipReference.xsd +0 -25
  101. package/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/sml.xsd +0 -4439
  102. package/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/vml-main.xsd +0 -570
  103. package/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/vml-officeDrawing.xsd +0 -509
  104. package/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/vml-presentationDrawing.xsd +0 -12
  105. package/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/vml-spreadsheetDrawing.xsd +0 -108
  106. package/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/vml-wordprocessingDrawing.xsd +0 -96
  107. package/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/wml.xsd +0 -3646
  108. package/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/xml.xsd +0 -116
  109. package/skills/docx/ooxml/schemas/ecma/fouth-edition/opc-contentTypes.xsd +0 -42
  110. package/skills/docx/ooxml/schemas/ecma/fouth-edition/opc-coreProperties.xsd +0 -50
  111. package/skills/docx/ooxml/schemas/ecma/fouth-edition/opc-digSig.xsd +0 -49
  112. package/skills/docx/ooxml/schemas/ecma/fouth-edition/opc-relationships.xsd +0 -33
  113. package/skills/docx/ooxml/schemas/mce/mc.xsd +0 -75
  114. package/skills/docx/ooxml/schemas/microsoft/wml-2010.xsd +0 -560
  115. package/skills/docx/ooxml/schemas/microsoft/wml-2012.xsd +0 -67
  116. package/skills/docx/ooxml/schemas/microsoft/wml-2018.xsd +0 -14
  117. package/skills/docx/ooxml/schemas/microsoft/wml-cex-2018.xsd +0 -20
  118. package/skills/docx/ooxml/schemas/microsoft/wml-cid-2016.xsd +0 -13
  119. package/skills/docx/ooxml/schemas/microsoft/wml-sdtdatahash-2020.xsd +0 -4
  120. package/skills/docx/ooxml/schemas/microsoft/wml-symex-2015.xsd +0 -8
  121. package/skills/docx/ooxml/scripts/pack.py +0 -159
  122. package/skills/docx/ooxml/scripts/unpack.py +0 -29
  123. package/skills/docx/ooxml/scripts/validate.py +0 -69
  124. package/skills/docx/ooxml/scripts/validation/__init__.py +0 -15
  125. package/skills/docx/ooxml/scripts/validation/base.py +0 -951
  126. package/skills/docx/ooxml/scripts/validation/docx.py +0 -274
  127. package/skills/docx/ooxml/scripts/validation/pptx.py +0 -315
  128. package/skills/docx/ooxml/scripts/validation/redlining.py +0 -279
  129. package/skills/docx/ooxml.md +0 -610
  130. package/skills/docx/scripts/__init__.py +0 -1
  131. package/skills/docx/scripts/document.py +0 -1276
  132. package/skills/docx/scripts/templates/comments.xml +0 -3
  133. package/skills/docx/scripts/templates/commentsExtended.xml +0 -3
  134. package/skills/docx/scripts/templates/commentsExtensible.xml +0 -3
  135. package/skills/docx/scripts/templates/commentsIds.xml +0 -3
  136. package/skills/docx/scripts/templates/people.xml +0 -3
  137. package/skills/docx/scripts/utilities.py +0 -374
  138. package/skills/pdf/LICENSE.txt +0 -30
  139. package/skills/pdf/SKILL.md +0 -294
  140. package/skills/pdf/forms.md +0 -205
  141. package/skills/pdf/reference.md +0 -612
  142. package/skills/pdf/scripts/check_bounding_boxes.py +0 -70
  143. package/skills/pdf/scripts/check_bounding_boxes_test.py +0 -226
  144. package/skills/pdf/scripts/check_fillable_fields.py +0 -12
  145. package/skills/pdf/scripts/convert_pdf_to_images.py +0 -35
  146. package/skills/pdf/scripts/create_validation_image.py +0 -41
  147. package/skills/pdf/scripts/extract_form_field_info.py +0 -152
  148. package/skills/pdf/scripts/fill_fillable_fields.py +0 -114
  149. package/skills/pdf/scripts/fill_pdf_form_with_annotations.py +0 -108
  150. package/skills/xlsx/LICENSE.txt +0 -30
  151. package/skills/xlsx/SKILL.md +0 -289
  152. package/skills/xlsx/recalc.py +0 -178
@@ -0,0 +1,915 @@
1
+ # React Best Practices - Compiled Rules
2
+
3
+ > Performance optimization guidelines for React 19 applications. Contains 35+ rules across 7 categories, prioritized by impact.
4
+
5
+ **Last Updated:** January 2026
6
+ **React Version:** 19
7
+ **Target:** Client-side SPA (Vite, etc.)
8
+
9
+ ---
10
+
11
+ ## Table of Contents
12
+
13
+ 1. [Eliminating Waterfalls](#1-eliminating-waterfalls-critical) (CRITICAL)
14
+ 2. [Bundle Size Optimization](#2-bundle-size-optimization-critical) (CRITICAL)
15
+ 3. [Client-Side Data Fetching](#3-client-side-data-fetching-medium-high) (MEDIUM-HIGH)
16
+ 4. [Re-render Optimization](#4-re-render-optimization-medium) (MEDIUM)
17
+ 5. [Rendering Performance](#5-rendering-performance-medium) (MEDIUM)
18
+ 6. [JavaScript Performance](#6-javascript-performance-low-medium) (LOW-MEDIUM)
19
+ 7. [Advanced Patterns](#7-advanced-patterns-low) (LOW)
20
+
21
+ ---
22
+
23
+ ## 1. Eliminating Waterfalls (CRITICAL)
24
+
25
+ Waterfalls are the #1 performance killer. Each sequential await adds full network latency. Eliminating them yields the largest gains.
26
+
27
+ ### Promise.all() for Independent Operations
28
+
29
+ **Impact:** CRITICAL (2-10× improvement)
30
+
31
+ When async operations have no interdependencies, execute them concurrently using `Promise.all()`.
32
+
33
+ **Incorrect (sequential execution, 3 round trips):**
34
+
35
+ ```typescript
36
+ const user = await fetchUser();
37
+ const posts = await fetchPosts();
38
+ const comments = await fetchComments();
39
+ ```
40
+
41
+ **Correct (parallel execution, 1 round trip):**
42
+
43
+ ```typescript
44
+ const [user, posts, comments] = await Promise.all([
45
+ fetchUser(),
46
+ fetchPosts(),
47
+ fetchComments(),
48
+ ]);
49
+ ```
50
+
51
+ ### Dependency-Based Parallelization
52
+
53
+ **Impact:** CRITICAL (2-10× improvement)
54
+
55
+ For operations with partial dependencies, use `better-all` to maximize parallelism. It automatically starts each task at the earliest possible moment.
56
+
57
+ **Incorrect (profile waits for config unnecessarily):**
58
+
59
+ ```typescript
60
+ const [user, config] = await Promise.all([fetchUser(), fetchConfig()]);
61
+ const profile = await fetchProfile(user.id);
62
+ ```
63
+
64
+ **Correct (config and profile run in parallel):**
65
+
66
+ ```typescript
67
+ import { all } from 'better-all';
68
+
69
+ const { user, config, profile } = await all({
70
+ async user() {
71
+ return fetchUser();
72
+ },
73
+ async config() {
74
+ return fetchConfig();
75
+ },
76
+ async profile() {
77
+ return fetchProfile((await this.$.user).id);
78
+ },
79
+ });
80
+ ```
81
+
82
+ Reference: [https://github.com/shuding/better-all](https://github.com/shuding/better-all)
83
+
84
+ ### Defer Await Until Needed
85
+
86
+ **Impact:** HIGH
87
+
88
+ Move `await` operations into the branches where they're actually used to avoid blocking code paths that don't need them.
89
+
90
+ **Incorrect (blocks both branches):**
91
+
92
+ ```typescript
93
+ async function handleRequest(userId: string, skipProcessing: boolean) {
94
+ const userData = await fetchUserData(userId);
95
+
96
+ if (skipProcessing) {
97
+ return { skipped: true };
98
+ }
99
+
100
+ return processUserData(userData);
101
+ }
102
+ ```
103
+
104
+ **Correct (only blocks when needed):**
105
+
106
+ ```typescript
107
+ async function handleRequest(userId: string, skipProcessing: boolean) {
108
+ if (skipProcessing) {
109
+ return { skipped: true };
110
+ }
111
+
112
+ const userData = await fetchUserData(userId);
113
+ return processUserData(userData);
114
+ }
115
+ ```
116
+
117
+ ### Strategic Suspense Boundaries
118
+
119
+ **Impact:** HIGH (faster initial paint)
120
+
121
+ Use Suspense boundaries to show wrapper UI immediately while data loads asynchronously.
122
+
123
+ **Incorrect (entire component waits for data):**
124
+
125
+ ```tsx
126
+ function Page() {
127
+ const { data, isLoading } = useQuery(['data'], fetchData);
128
+
129
+ if (isLoading) return <Skeleton />;
130
+
131
+ return (
132
+ <div>
133
+ <div>Sidebar</div>
134
+ <div>Header</div>
135
+ <div>
136
+ <DataDisplay data={data} />
137
+ </div>
138
+ <div>Footer</div>
139
+ </div>
140
+ );
141
+ }
142
+ ```
143
+
144
+ **Correct (wrapper shows immediately):**
145
+
146
+ ```tsx
147
+ function Page() {
148
+ return (
149
+ <div>
150
+ <div>Sidebar</div>
151
+ <div>Header</div>
152
+ <Suspense fallback={<Skeleton />}>
153
+ <DataDisplay />
154
+ </Suspense>
155
+ <div>Footer</div>
156
+ </div>
157
+ );
158
+ }
159
+
160
+ function DataDisplay() {
161
+ const { data } = useSuspenseQuery(['data'], fetchData);
162
+ return <div>{data.content}</div>;
163
+ }
164
+ ```
165
+
166
+ **With React 19 use() hook:**
167
+
168
+ ```tsx
169
+ function Page() {
170
+ const dataPromise = useMemo(() => fetchData(), []);
171
+
172
+ return (
173
+ <Suspense fallback={<Skeleton />}>
174
+ <DataDisplay dataPromise={dataPromise} />
175
+ </Suspense>
176
+ );
177
+ }
178
+
179
+ function DataDisplay({ dataPromise }: { dataPromise: Promise<Data> }) {
180
+ const data = use(dataPromise);
181
+ return <div>{data.content}</div>;
182
+ }
183
+ ```
184
+
185
+ ---
186
+
187
+ ## 2. Bundle Size Optimization (CRITICAL)
188
+
189
+ Reducing initial bundle size improves Time to Interactive and Largest Contentful Paint.
190
+
191
+ ### Avoid Barrel File Imports
192
+
193
+ **Impact:** CRITICAL (200-800ms import cost)
194
+
195
+ Import directly from source files instead of barrel files to avoid loading thousands of unused modules.
196
+
197
+ **Incorrect (imports entire library):**
198
+
199
+ ```tsx
200
+ import { Check, X, Menu } from 'lucide-react';
201
+ // Loads 1,583 modules, takes ~2.8s extra in dev
202
+
203
+ import { Button, TextField } from '@mui/material';
204
+ // Loads 2,225 modules, takes ~4.2s extra in dev
205
+ ```
206
+
207
+ **Correct (imports only what you need):**
208
+
209
+ ```tsx
210
+ import Check from 'lucide-react/dist/esm/icons/check';
211
+ import X from 'lucide-react/dist/esm/icons/x';
212
+ import Menu from 'lucide-react/dist/esm/icons/menu';
213
+
214
+ import Button from '@mui/material/Button';
215
+ import TextField from '@mui/material/TextField';
216
+ ```
217
+
218
+ Commonly affected libraries: `lucide-react`, `@mui/material`, `@mui/icons-material`, `@tabler/icons-react`, `react-icons`, `lodash`, `date-fns`.
219
+
220
+ ### Conditional Module Loading
221
+
222
+ **Impact:** HIGH
223
+
224
+ Load large data or modules only when a feature is activated.
225
+
226
+ **React 19 with use() and Suspense (Recommended):**
227
+
228
+ ```tsx
229
+ import { use, Suspense, useMemo } from 'react';
230
+
231
+ function AnimationPlayer({ enabled }: { enabled: boolean }) {
232
+ const framesPromise = useMemo(
233
+ () =>
234
+ enabled ? import('./animation-frames.js').then((m) => m.frames) : null,
235
+ [enabled]
236
+ );
237
+
238
+ if (!framesPromise) return null;
239
+
240
+ const frames = use(framesPromise);
241
+ return <Canvas frames={frames} />;
242
+ }
243
+
244
+ function AnimationSection({ enabled }: { enabled: boolean }) {
245
+ return (
246
+ <Suspense fallback={<Skeleton />}>
247
+ <AnimationPlayer enabled={enabled} />
248
+ </Suspense>
249
+ );
250
+ }
251
+ ```
252
+
253
+ **With React.lazy:**
254
+
255
+ ```tsx
256
+ const HeavyEditor = lazy(() => import('./HeavyEditor'));
257
+
258
+ function EditorSection({ showEditor }: { showEditor: boolean }) {
259
+ if (!showEditor) return null;
260
+
261
+ return (
262
+ <Suspense fallback={<EditorSkeleton />}>
263
+ <HeavyEditor />
264
+ </Suspense>
265
+ );
266
+ }
267
+ ```
268
+
269
+ ### Preload Based on User Intent
270
+
271
+ **Impact:** MEDIUM
272
+
273
+ Preload heavy bundles on hover/focus to reduce perceived latency.
274
+
275
+ ```tsx
276
+ function EditorButton({ onClick }: { onClick: () => void }) {
277
+ const preload = () => {
278
+ void import('./monaco-editor');
279
+ };
280
+
281
+ return (
282
+ <button onMouseEnter={preload} onFocus={preload} onClick={onClick}>
283
+ Open Editor
284
+ </button>
285
+ );
286
+ }
287
+ ```
288
+
289
+ ---
290
+
291
+ ## 3. Client-Side Data Fetching (MEDIUM-HIGH)
292
+
293
+ Automatic deduplication and efficient data fetching patterns reduce redundant network requests.
294
+
295
+ ### Use React Query for Automatic Deduplication
296
+
297
+ **Impact:** MEDIUM-HIGH
298
+
299
+ TanStack Query (React Query 5) enables request deduplication, caching, and stale-while-revalidate patterns.
300
+
301
+ **Incorrect (each instance fetches):**
302
+
303
+ ```tsx
304
+ function UserList() {
305
+ const [users, setUsers] = useState<User[]>([]);
306
+ const [isLoading, setIsLoading] = useState(true);
307
+
308
+ useEffect(() => {
309
+ fetch('/api/users')
310
+ .then((r) => r.json())
311
+ .then(setUsers)
312
+ .finally(() => setIsLoading(false));
313
+ }, []);
314
+
315
+ // ...
316
+ }
317
+ ```
318
+
319
+ **Correct (multiple instances share one request):**
320
+
321
+ ```tsx
322
+ import { useQuery } from '@tanstack/react-query';
323
+
324
+ function UserList() {
325
+ const { data: users, isLoading } = useQuery({
326
+ queryKey: ['users'],
327
+ queryFn: () => fetch('/api/users').then((r) => r.json()),
328
+ });
329
+
330
+ if (isLoading) return <Skeleton />;
331
+ return (
332
+ <ul>
333
+ {users.map((u) => (
334
+ <li key={u.id}>{u.name}</li>
335
+ ))}
336
+ </ul>
337
+ );
338
+ }
339
+ ```
340
+
341
+ **With Suspense (React 19):**
342
+
343
+ ```tsx
344
+ import { useSuspenseQuery } from '@tanstack/react-query';
345
+
346
+ function UserList() {
347
+ const { data: users } = useSuspenseQuery({
348
+ queryKey: ['users'],
349
+ queryFn: fetchUsers,
350
+ });
351
+
352
+ return (
353
+ <ul>
354
+ {users.map((u) => (
355
+ <li key={u.id}>{u.name}</li>
356
+ ))}
357
+ </ul>
358
+ );
359
+ }
360
+
361
+ // Parent
362
+ <Suspense fallback={<Skeleton />}>
363
+ <UserList />
364
+ </Suspense>;
365
+ ```
366
+
367
+ ### Deduplicate Global Event Listeners
368
+
369
+ **Impact:** MEDIUM (single listener for N components)
370
+
371
+ Use `useSyncExternalStore` to share global event listeners across component instances.
372
+
373
+ **Incorrect (N instances = N listeners):**
374
+
375
+ ```tsx
376
+ function useKeyboardShortcut(key: string, callback: () => void) {
377
+ useEffect(() => {
378
+ const handler = (e: KeyboardEvent) => {
379
+ if (e.metaKey && e.key === key) callback();
380
+ };
381
+ window.addEventListener('keydown', handler);
382
+ return () => window.removeEventListener('keydown', handler);
383
+ }, [key, callback]);
384
+ }
385
+ ```
386
+
387
+ **Correct (N instances = 1 listener):**
388
+
389
+ ```tsx
390
+ import { useSyncExternalStore } from 'react';
391
+
392
+ function useOnlineStatus() {
393
+ return useSyncExternalStore(
394
+ (callback) => {
395
+ window.addEventListener('online', callback);
396
+ window.addEventListener('offline', callback);
397
+ return () => {
398
+ window.removeEventListener('online', callback);
399
+ window.removeEventListener('offline', callback);
400
+ };
401
+ },
402
+ () => navigator.onLine,
403
+ () => true
404
+ );
405
+ }
406
+ ```
407
+
408
+ Reference: [React useSyncExternalStore](https://react.dev/reference/react/useSyncExternalStore)
409
+
410
+ ---
411
+
412
+ ## 4. Re-render Optimization (MEDIUM)
413
+
414
+ Reducing unnecessary re-renders minimizes wasted computation and improves UI responsiveness.
415
+
416
+ ### Extract to Memoized Components
417
+
418
+ **Impact:** MEDIUM
419
+
420
+ **With React 19 Compiler (Recommended):**
421
+
422
+ The compiler automatically memoizes components. Just write clean code:
423
+
424
+ ```tsx
425
+ function UserAvatar({ user }: { user: User }) {
426
+ const id = computeAvatarId(user);
427
+ return <Avatar id={id} />;
428
+ }
429
+ ```
430
+
431
+ **Without React Compiler (Manual):**
432
+
433
+ ```tsx
434
+ const UserAvatar = memo(function UserAvatar({ user }: { user: User }) {
435
+ const id = useMemo(() => computeAvatarId(user), [user]);
436
+ return <Avatar id={id} />;
437
+ });
438
+ ```
439
+
440
+ ### Use Functional setState Updates
441
+
442
+ **Impact:** MEDIUM
443
+
444
+ Use functional update form to prevent stale closures and create stable callbacks.
445
+
446
+ **Incorrect:**
447
+
448
+ ```tsx
449
+ const addItems = useCallback(
450
+ (newItems: Item[]) => {
451
+ setItems([...items, ...newItems]);
452
+ },
453
+ [items]
454
+ ); // Recreated on every items change
455
+ ```
456
+
457
+ **Correct:**
458
+
459
+ ```tsx
460
+ const addItems = useCallback((newItems: Item[]) => {
461
+ setItems((curr) => [...curr, ...newItems]);
462
+ }, []); // Stable, never recreated
463
+ ```
464
+
465
+ ### Use Lazy State Initialization
466
+
467
+ **Impact:** MEDIUM
468
+
469
+ Pass a function to `useState` for expensive initial values.
470
+
471
+ **Incorrect (runs on every render):**
472
+
473
+ ```tsx
474
+ const [searchIndex] = useState(buildSearchIndex(items));
475
+ ```
476
+
477
+ **Correct (runs only once):**
478
+
479
+ ```tsx
480
+ const [searchIndex] = useState(() => buildSearchIndex(items));
481
+ ```
482
+
483
+ ### Use Transitions for Non-Urgent Updates
484
+
485
+ **Impact:** MEDIUM
486
+
487
+ Mark frequent, non-urgent state updates as transitions.
488
+
489
+ ```tsx
490
+ import { startTransition } from 'react';
491
+
492
+ const handler = () => {
493
+ startTransition(() => setScrollY(window.scrollY));
494
+ };
495
+ window.addEventListener('scroll', handler, { passive: true });
496
+ ```
497
+
498
+ ### Subscribe to Derived State
499
+
500
+ **Impact:** MEDIUM
501
+
502
+ Subscribe to derived boolean state instead of continuous values.
503
+
504
+ **Incorrect (re-renders on every pixel):**
505
+
506
+ ```tsx
507
+ const width = useWindowWidth();
508
+ const isMobile = width < 768;
509
+ ```
510
+
511
+ **Correct (re-renders only on boolean change):**
512
+
513
+ ```tsx
514
+ const isMobile = useMediaQuery('(max-width: 767px)');
515
+ ```
516
+
517
+ ### Defer State Reads to Usage Point
518
+
519
+ **Impact:** MEDIUM
520
+
521
+ Don't subscribe to dynamic state if you only read it inside callbacks.
522
+
523
+ **Incorrect:**
524
+
525
+ ```tsx
526
+ const searchParams = useSearchParams();
527
+ const handleShare = () => {
528
+ const ref = searchParams.get('ref');
529
+ shareChat(chatId, { ref });
530
+ };
531
+ ```
532
+
533
+ **Correct:**
534
+
535
+ ```tsx
536
+ const handleShare = () => {
537
+ const params = new URLSearchParams(window.location.search);
538
+ const ref = params.get('ref');
539
+ shareChat(chatId, { ref });
540
+ };
541
+ ```
542
+
543
+ ### Narrow Effect Dependencies
544
+
545
+ **Impact:** LOW
546
+
547
+ Specify primitive dependencies instead of objects.
548
+
549
+ **Incorrect:**
550
+
551
+ ```tsx
552
+ useEffect(() => {
553
+ console.log(user.id);
554
+ }, [user]);
555
+ ```
556
+
557
+ **Correct:**
558
+
559
+ ```tsx
560
+ useEffect(() => {
561
+ console.log(user.id);
562
+ }, [user.id]);
563
+ ```
564
+
565
+ ---
566
+
567
+ ## 5. Rendering Performance (MEDIUM)
568
+
569
+ Optimizing the rendering process reduces the work the browser needs to do.
570
+
571
+ ### CSS content-visibility for Long Lists
572
+
573
+ **Impact:** HIGH (10× faster initial render)
574
+
575
+ ```css
576
+ .message-item {
577
+ content-visibility: auto;
578
+ contain-intrinsic-size: 0 80px;
579
+ }
580
+ ```
581
+
582
+ ### Preserve State for Hidden Components
583
+
584
+ **Impact:** MEDIUM
585
+
586
+ Use CSS visibility instead of conditional rendering to preserve state.
587
+
588
+ **Incorrect (unmounts, loses state):**
589
+
590
+ ```tsx
591
+ return isOpen ? <ExpensiveMenu /> : null;
592
+ ```
593
+
594
+ **Correct (stays mounted):**
595
+
596
+ ```tsx
597
+ <div style={{ display: isOpen ? 'block' : 'none' }}>
598
+ <ExpensiveMenu />
599
+ </div>
600
+ ```
601
+
602
+ ### Hoist Static JSX Elements
603
+
604
+ **Impact:** LOW
605
+
606
+ **With React 19 Compiler:** Automatic - just write normal components.
607
+
608
+ **Without Compiler:**
609
+
610
+ ```tsx
611
+ // Hoisted to module scope
612
+ const loadingSkeleton = <div className="animate-pulse h-20 bg-gray-200" />;
613
+
614
+ function Container({ loading }: { loading: boolean }) {
615
+ return <div>{loading && loadingSkeleton}</div>;
616
+ }
617
+ ```
618
+
619
+ ### Animate SVG Wrapper Instead of SVG
620
+
621
+ **Impact:** LOW (enables hardware acceleration)
622
+
623
+ ```tsx
624
+ // Incorrect
625
+ <svg className="animate-spin">...</svg>
626
+
627
+ // Correct
628
+ <div className="animate-spin">
629
+ <svg>...</svg>
630
+ </div>
631
+ ```
632
+
633
+ ### Use Explicit Conditional Rendering
634
+
635
+ **Impact:** LOW (prevents rendering 0 or NaN)
636
+
637
+ ```tsx
638
+ // Incorrect (renders "0")
639
+ {
640
+ count && <span>{count}</span>;
641
+ }
642
+
643
+ // Correct
644
+ {
645
+ count > 0 ? <span>{count}</span> : null;
646
+ }
647
+ ```
648
+
649
+ ### Optimize SVG Precision
650
+
651
+ **Impact:** LOW
652
+
653
+ ```bash
654
+ npx svgo --precision=1 --multipass icon.svg
655
+ ```
656
+
657
+ ---
658
+
659
+ ## 6. JavaScript Performance (LOW-MEDIUM)
660
+
661
+ Micro-optimizations for hot paths can add up to meaningful improvements.
662
+
663
+ ### Use toSorted() for Immutability
664
+
665
+ **Impact:** MEDIUM-HIGH
666
+
667
+ ```typescript
668
+ // Incorrect (mutates array)
669
+ const sorted = users.sort((a, b) => a.name.localeCompare(b.name));
670
+
671
+ // Correct (creates new array)
672
+ const sorted = users.toSorted((a, b) => a.name.localeCompare(b.name));
673
+ ```
674
+
675
+ ### Cache Repeated Function Calls
676
+
677
+ **Impact:** MEDIUM
678
+
679
+ ```typescript
680
+ const slugifyCache = new Map<string, string>();
681
+
682
+ function cachedSlugify(text: string): string {
683
+ if (slugifyCache.has(text)) return slugifyCache.get(text)!;
684
+ const result = slugify(text);
685
+ slugifyCache.set(text, result);
686
+ return result;
687
+ }
688
+ ```
689
+
690
+ ### Batch DOM CSS Changes
691
+
692
+ **Impact:** MEDIUM
693
+
694
+ ```tsx
695
+ // Incorrect (multiple reflows)
696
+ element.style.width = '100px';
697
+ element.style.height = '200px';
698
+
699
+ // Correct (single reflow)
700
+ element.classList.add('highlighted-box');
701
+ ```
702
+
703
+ ### Early Length Check for Array Comparisons
704
+
705
+ **Impact:** MEDIUM-HIGH
706
+
707
+ ```typescript
708
+ function hasChanges(current: string[], original: string[]) {
709
+ if (current.length !== original.length) return true;
710
+ // ... expensive comparison
711
+ }
712
+ ```
713
+
714
+ ### Use Set/Map for O(1) Lookups
715
+
716
+ **Impact:** LOW-MEDIUM
717
+
718
+ ```typescript
719
+ // Incorrect O(n)
720
+ items.filter((item) => allowedIds.includes(item.id));
721
+
722
+ // Correct O(1)
723
+ const allowedSet = new Set(allowedIds);
724
+ items.filter((item) => allowedSet.has(item.id));
725
+ ```
726
+
727
+ ### Build Index Maps for Repeated Lookups
728
+
729
+ **Impact:** LOW-MEDIUM
730
+
731
+ ```typescript
732
+ const userById = new Map(users.map((u) => [u.id, u]));
733
+ orders.map((order) => ({ ...order, user: userById.get(order.userId) }));
734
+ ```
735
+
736
+ ### Use Loop for Min/Max Instead of Sort
737
+
738
+ **Impact:** LOW (O(n) instead of O(n log n))
739
+
740
+ ```typescript
741
+ let latest = projects[0];
742
+ for (let i = 1; i < projects.length; i++) {
743
+ if (projects[i].updatedAt > latest.updatedAt) latest = projects[i];
744
+ }
745
+ ```
746
+
747
+ ### Cache Storage API Calls
748
+
749
+ **Impact:** LOW-MEDIUM
750
+
751
+ ```typescript
752
+ const storageCache = new Map<string, string | null>();
753
+
754
+ function getLocalStorage(key: string) {
755
+ if (!storageCache.has(key)) {
756
+ storageCache.set(key, localStorage.getItem(key));
757
+ }
758
+ return storageCache.get(key);
759
+ }
760
+ ```
761
+
762
+ ### Early Return from Functions
763
+
764
+ **Impact:** LOW-MEDIUM
765
+
766
+ ```typescript
767
+ function validateUsers(users: User[]) {
768
+ for (const user of users) {
769
+ if (!user.email) return { valid: false, error: 'Email required' };
770
+ }
771
+ return { valid: true };
772
+ }
773
+ ```
774
+
775
+ ### Combine Multiple Array Iterations
776
+
777
+ **Impact:** LOW-MEDIUM
778
+
779
+ ```typescript
780
+ // Incorrect (3 iterations)
781
+ const admins = users.filter((u) => u.isAdmin);
782
+ const testers = users.filter((u) => u.isTester);
783
+
784
+ // Correct (1 iteration)
785
+ const admins: User[] = [],
786
+ testers: User[] = [];
787
+ for (const user of users) {
788
+ if (user.isAdmin) admins.push(user);
789
+ if (user.isTester) testers.push(user);
790
+ }
791
+ ```
792
+
793
+ ### Hoist RegExp Creation
794
+
795
+ **Impact:** LOW-MEDIUM
796
+
797
+ ```tsx
798
+ const EMAIL_REGEX = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
799
+
800
+ function Highlighter({ text, query }: Props) {
801
+ const regex = useMemo(() => new RegExp(`(${query})`, 'gi'), [query]);
802
+ // ...
803
+ }
804
+ ```
805
+
806
+ ### Cache Property Access in Loops
807
+
808
+ **Impact:** LOW-MEDIUM
809
+
810
+ ```typescript
811
+ const value = obj.config.settings.value;
812
+ const len = arr.length;
813
+ for (let i = 0; i < len; i++) {
814
+ process(value);
815
+ }
816
+ ```
817
+
818
+ ---
819
+
820
+ ## 7. Advanced Patterns (LOW)
821
+
822
+ Advanced patterns for specific cases that require careful implementation.
823
+
824
+ ### Stable Event Callbacks with useEffectEvent
825
+
826
+ **Impact:** MEDIUM
827
+
828
+ Use `useEffectEvent` (React 19) to access latest values in effect callbacks without adding them to dependency arrays.
829
+
830
+ **Incorrect:**
831
+
832
+ ```tsx
833
+ useEffect(() => {
834
+ const timeout = setTimeout(() => onSearch(query), 300);
835
+ return () => clearTimeout(timeout);
836
+ }, [query, onSearch]); // onSearch causes unnecessary re-runs
837
+ ```
838
+
839
+ **Correct (React 19):**
840
+
841
+ ```tsx
842
+ import { useEffectEvent } from 'react';
843
+
844
+ const onSearchEvent = useEffectEvent((q: string) => onSearch(q));
845
+
846
+ useEffect(() => {
847
+ const timeout = setTimeout(() => onSearchEvent(query), 300);
848
+ return () => clearTimeout(timeout);
849
+ }, [query]); // No need to include onSearchEvent
850
+ ```
851
+
852
+ **Legacy fallback (pre-React 19):**
853
+
854
+ ```tsx
855
+ function useLatest<T>(value: T) {
856
+ const ref = useRef(value);
857
+ useEffect(() => {
858
+ ref.current = value;
859
+ }, [value]);
860
+ return ref;
861
+ }
862
+
863
+ const onSearchRef = useLatest(onSearch);
864
+ useEffect(() => {
865
+ const timeout = setTimeout(() => onSearchRef.current(query), 300);
866
+ return () => clearTimeout(timeout);
867
+ }, [query]);
868
+ ```
869
+
870
+ ### Stable Event Handler References
871
+
872
+ **Impact:** MEDIUM
873
+
874
+ Use `useEffectEvent` for stable event handler references in effects.
875
+
876
+ **React 19:**
877
+
878
+ ```tsx
879
+ import { useEffectEvent } from 'react';
880
+
881
+ function useWindowEvent(event: string, handler: () => void) {
882
+ const onEvent = useEffectEvent(handler);
883
+
884
+ useEffect(() => {
885
+ window.addEventListener(event, onEvent);
886
+ return () => window.removeEventListener(event, onEvent);
887
+ }, [event]);
888
+ }
889
+ ```
890
+
891
+ **Legacy fallback:**
892
+
893
+ ```tsx
894
+ function useWindowEvent(event: string, handler: () => void) {
895
+ const handlerRef = useRef(handler);
896
+ useEffect(() => {
897
+ handlerRef.current = handler;
898
+ }, [handler]);
899
+
900
+ useEffect(() => {
901
+ const listener = () => handlerRef.current();
902
+ window.addEventListener(event, listener);
903
+ return () => window.removeEventListener(event, listener);
904
+ }, [event]);
905
+ }
906
+ ```
907
+
908
+ ---
909
+
910
+ ## References
911
+
912
+ - [React Compiler](https://react.dev/learn/react-compiler)
913
+ - [TanStack Query](https://tanstack.com/query)
914
+ - [useSyncExternalStore](https://react.dev/reference/react/useSyncExternalStore)
915
+ - [useEffectEvent RFC](https://react.dev/learn/separating-events-from-effects)