agent-skill-kit 3.9.135

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 (615) hide show
  1. package/.agent/global.d.ts +80 -0
  2. package/.agent/rules/GEMINI.md +210 -0
  3. package/.agent/rules/autopilot.md +287 -0
  4. package/.agent/rules/code-rules.md +227 -0
  5. package/.agent/scripts/audit_workflows.ts +23 -0
  6. package/.agent/scripts/auto_preview.ts +170 -0
  7. package/.agent/scripts/checklist.ts +180 -0
  8. package/.agent/scripts/compile-agents.ts +237 -0
  9. package/.agent/scripts/fix_skills.ts +49 -0
  10. package/.agent/scripts/session_manager.ts +222 -0
  11. package/.agent/scripts/skill-audit.ts +255 -0
  12. package/.agent/scripts/sync_workflows.ts +54 -0
  13. package/.agent/scripts/utils/colors.ts +58 -0
  14. package/.agent/scripts/utils/process-manager.ts +131 -0
  15. package/.agent/scripts/utils/reporter.ts +192 -0
  16. package/.agent/scripts/utils/runner.ts +128 -0
  17. package/.agent/scripts/verify_all.ts +243 -0
  18. package/.agent/scripts/version-sync.ts +256 -0
  19. package/.agent/skills/SKILL_INDEX.md +129 -0
  20. package/.agent/skills/agent-browser/AGENTS.md +728 -0
  21. package/.agent/skills/agent-browser/SKILL.md +193 -0
  22. package/.agent/skills/agent-browser/rules/_sections.md +15 -0
  23. package/.agent/skills/agent-browser/rules/_template.md +32 -0
  24. package/.agent/skills/agent-browser/rules/engineering-spec.md +528 -0
  25. package/.agent/skills/agent-browser/scripts/browser_cli.ts +52 -0
  26. package/.agent/skills/agent-browser/scripts/session_manager.ts +166 -0
  27. package/.agent/skills/ai-artist/AGENTS.md +1082 -0
  28. package/.agent/skills/ai-artist/SKILL.md +186 -0
  29. package/.agent/skills/ai-artist/rules/_sections.md +30 -0
  30. package/.agent/skills/ai-artist/rules/_template.md +32 -0
  31. package/.agent/skills/ai-artist/rules/domain-code.md +118 -0
  32. package/.agent/skills/ai-artist/rules/domain-marketing.md +105 -0
  33. package/.agent/skills/ai-artist/rules/engineering-spec.md +519 -0
  34. package/.agent/skills/ai-artist/rules/image-prompts.md +195 -0
  35. package/.agent/skills/ai-artist/rules/model-syntax.md +115 -0
  36. package/.agent/skills/ai-artist/scripts/prompt_compiler.ts +72 -0
  37. package/.agent/skills/ai-artist/templates/image-core.txt +1 -0
  38. package/.agent/skills/ai-artist/templates/llm-core.txt +6 -0
  39. package/.agent/skills/api-architect/AGENTS.md +1896 -0
  40. package/.agent/skills/api-architect/SKILL.md +173 -0
  41. package/.agent/skills/api-architect/rules/_sections.md +35 -0
  42. package/.agent/skills/api-architect/rules/_template.md +32 -0
  43. package/.agent/skills/api-architect/rules/api-style.md +115 -0
  44. package/.agent/skills/api-architect/rules/auth.md +134 -0
  45. package/.agent/skills/api-architect/rules/documentation.md +131 -0
  46. package/.agent/skills/api-architect/rules/engineering-spec.md +505 -0
  47. package/.agent/skills/api-architect/rules/graphql.md +154 -0
  48. package/.agent/skills/api-architect/rules/rate-limiting.md +76 -0
  49. package/.agent/skills/api-architect/rules/response.md +138 -0
  50. package/.agent/skills/api-architect/rules/rest.md +113 -0
  51. package/.agent/skills/api-architect/rules/security-testing.md +146 -0
  52. package/.agent/skills/api-architect/rules/trpc.md +129 -0
  53. package/.agent/skills/api-architect/rules/versioning.md +100 -0
  54. package/.agent/skills/api-architect/scripts/api_validator.ts +413 -0
  55. package/.agent/skills/auth-patterns/AGENTS.md +1830 -0
  56. package/.agent/skills/auth-patterns/SKILL.md +163 -0
  57. package/.agent/skills/auth-patterns/rules/_sections.md +30 -0
  58. package/.agent/skills/auth-patterns/rules/_template.md +32 -0
  59. package/.agent/skills/auth-patterns/rules/engineering-spec.md +515 -0
  60. package/.agent/skills/auth-patterns/rules/jwt-deep.md +196 -0
  61. package/.agent/skills/auth-patterns/rules/mfa.md +174 -0
  62. package/.agent/skills/auth-patterns/rules/oauth2.md +134 -0
  63. package/.agent/skills/auth-patterns/rules/passkey.md +243 -0
  64. package/.agent/skills/auth-patterns/rules/rbac-abac.md +206 -0
  65. package/.agent/skills/auth-patterns/rules/session.md +183 -0
  66. package/.agent/skills/auth-patterns/scripts/auth_validator.ts +121 -0
  67. package/.agent/skills/chrome-devtools/AGENTS.md +952 -0
  68. package/.agent/skills/chrome-devtools/SKILL.md +160 -0
  69. package/.agent/skills/chrome-devtools/rules/_sections.md +25 -0
  70. package/.agent/skills/chrome-devtools/rules/_template.md +32 -0
  71. package/.agent/skills/chrome-devtools/rules/aria-snapshot.md +95 -0
  72. package/.agent/skills/chrome-devtools/rules/engineering-spec.md +510 -0
  73. package/.agent/skills/chrome-devtools/rules/scripts-guide.md +174 -0
  74. package/.agent/skills/chrome-devtools/scripts/aria-snapshot.ts +3 -0
  75. package/.agent/skills/chrome-devtools/scripts/click.ts +3 -0
  76. package/.agent/skills/chrome-devtools/scripts/console.ts +3 -0
  77. package/.agent/skills/chrome-devtools/scripts/core_cli.ts +79 -0
  78. package/.agent/skills/chrome-devtools/scripts/evaluate.ts +3 -0
  79. package/.agent/skills/chrome-devtools/scripts/fill.ts +3 -0
  80. package/.agent/skills/chrome-devtools/scripts/navigate.ts +3 -0
  81. package/.agent/skills/chrome-devtools/scripts/network.ts +3 -0
  82. package/.agent/skills/chrome-devtools/scripts/performance.ts +3 -0
  83. package/.agent/skills/chrome-devtools/scripts/screenshot.ts +3 -0
  84. package/.agent/skills/chrome-devtools/scripts/select-ref.ts +3 -0
  85. package/.agent/skills/cicd-pipeline/AGENTS.md +809 -0
  86. package/.agent/skills/cicd-pipeline/SKILL.md +164 -0
  87. package/.agent/skills/cicd-pipeline/rules/_sections.md +15 -0
  88. package/.agent/skills/cicd-pipeline/rules/_template.md +32 -0
  89. package/.agent/skills/cicd-pipeline/rules/engineering-spec.md +477 -0
  90. package/.agent/skills/cicd-pipeline/scripts/flag-manager.ts +253 -0
  91. package/.agent/skills/cicd-pipeline/scripts/pipeline_validator.ts +133 -0
  92. package/.agent/skills/code-constitution/AGENTS.md +597 -0
  93. package/.agent/skills/code-constitution/CHANGELOG.md +216 -0
  94. package/.agent/skills/code-constitution/SKILL.md +191 -0
  95. package/.agent/skills/code-constitution/VERSION +3 -0
  96. package/.agent/skills/code-constitution/examples/violation-backend-mutation/after.tsx +59 -0
  97. package/.agent/skills/code-constitution/examples/violation-backend-mutation/before.tsx +42 -0
  98. package/.agent/skills/code-constitution/examples/violation-backend-mutation/explanation.md +91 -0
  99. package/.agent/skills/code-constitution/examples/violation-chart-injection/after.tsx +99 -0
  100. package/.agent/skills/code-constitution/examples/violation-chart-injection/before.tsx +57 -0
  101. package/.agent/skills/code-constitution/examples/violation-chart-injection/explanation.md +120 -0
  102. package/.agent/skills/code-constitution/knowledge/lessons-learned.yaml +3 -0
  103. package/.agent/skills/code-constitution/metadata/precedence.yaml +117 -0
  104. package/.agent/skills/code-constitution/metadata/scope-map.yaml +156 -0
  105. package/.agent/skills/code-constitution/proposals/v1.1-change-proposal-template.md +201 -0
  106. package/.agent/skills/code-constitution/resources/AUTHORITY_MODEL.md +115 -0
  107. package/.agent/skills/code-constitution/resources/ENFORCEMENT_GUIDE.md +246 -0
  108. package/.agent/skills/code-constitution/resources/LOAD_ORDER.md +86 -0
  109. package/.agent/skills/code-constitution/rules/_sections.md +15 -0
  110. package/.agent/skills/code-constitution/rules/_template.md +32 -0
  111. package/.agent/skills/code-constitution/rules/constitution/master-constitution.md +210 -0
  112. package/.agent/skills/code-constitution/rules/doctrines/architecture/architecture-doctrine.md +188 -0
  113. package/.agent/skills/code-constitution/rules/doctrines/backend/backend-data-engine-doctrine.md +218 -0
  114. package/.agent/skills/code-constitution/rules/doctrines/commercial/commercial-guardrails-doctrine.md +196 -0
  115. package/.agent/skills/code-constitution/rules/doctrines/data/data-integrity-doctrine.md +202 -0
  116. package/.agent/skills/code-constitution/rules/doctrines/frontend/frontend-mobile-doctrine.md +169 -0
  117. package/.agent/skills/code-constitution/rules/doctrines/frontend/interaction-patterns-doctrine.md +176 -0
  118. package/.agent/skills/code-constitution/rules/doctrines/learning/learning-engine-doctrine.md +192 -0
  119. package/.agent/skills/code-constitution/rules/doctrines/performance/performance-doctrine.md +180 -0
  120. package/.agent/skills/code-constitution/rules/doctrines/review/code-review-doctrine.md +174 -0
  121. package/.agent/skills/code-constitution/rules/enforcement/agents/agent-enforcement-protocol.md +218 -0
  122. package/.agent/skills/code-constitution/rules/enforcement/agents/agent-system-prompt.md +196 -0
  123. package/.agent/skills/code-constitution/rules/enforcement/checklists/backend-api-review-checklist.md +131 -0
  124. package/.agent/skills/code-constitution/rules/enforcement/checklists/chart-component-review-checklist.md +147 -0
  125. package/.agent/skills/code-constitution/rules/enforcement/checklists/frontend-review-checklist.md +194 -0
  126. package/.agent/skills/code-constitution/rules/enforcement/playbooks/doctrine-violation-playbook.md +236 -0
  127. package/.agent/skills/code-constitution/rules/engineering-spec.md +561 -0
  128. package/.agent/skills/code-constitution/scripts/audit_pr.ts +219 -0
  129. package/.agent/skills/code-constitution/scripts/check_boundaries.ts +134 -0
  130. package/.agent/skills/code-constitution/scripts/learn.ts +202 -0
  131. package/.agent/skills/code-constitution/scripts/validate_doctrine.ts +287 -0
  132. package/.agent/skills/code-craft/AGENTS.md +803 -0
  133. package/.agent/skills/code-craft/SKILL.md +170 -0
  134. package/.agent/skills/code-craft/rules/_sections.md +20 -0
  135. package/.agent/skills/code-craft/rules/_template.md +32 -0
  136. package/.agent/skills/code-craft/rules/engineering-spec.md +447 -0
  137. package/.agent/skills/code-craft/rules/verification-scripts.md +83 -0
  138. package/.agent/skills/code-craft/scripts/code_quality_checker.ts +193 -0
  139. package/.agent/skills/code-review/AGENTS.md +1664 -0
  140. package/.agent/skills/code-review/SKILL.md +152 -0
  141. package/.agent/skills/code-review/rules/_sections.md +15 -0
  142. package/.agent/skills/code-review/rules/_template.md +32 -0
  143. package/.agent/skills/code-review/rules/engineering-spec.md +466 -0
  144. package/.agent/skills/code-review/scripts/lint_runner.ts +213 -0
  145. package/.agent/skills/code-review/scripts/type_coverage.ts +118 -0
  146. package/.agent/skills/context-engineering/AGENTS.md +499 -0
  147. package/.agent/skills/context-engineering/SKILL.md +147 -0
  148. package/.agent/skills/context-engineering/rules/_sections.md +15 -0
  149. package/.agent/skills/context-engineering/rules/_template.md +32 -0
  150. package/.agent/skills/context-engineering/rules/engineering-spec.md +463 -0
  151. package/.agent/skills/context-engineering/scripts/context_analyzer.ts +127 -0
  152. package/.agent/skills/copywriting/AGENTS.md +501 -0
  153. package/.agent/skills/copywriting/SKILL.md +188 -0
  154. package/.agent/skills/copywriting/rules/_sections.md +15 -0
  155. package/.agent/skills/copywriting/rules/_template.md +32 -0
  156. package/.agent/skills/copywriting/rules/engineering-spec.md +465 -0
  157. package/.agent/skills/copywriting/scripts/copy_validator.ts +185 -0
  158. package/.agent/skills/data-modeler/AGENTS.md +814 -0
  159. package/.agent/skills/data-modeler/SKILL.md +195 -0
  160. package/.agent/skills/data-modeler/rules/_sections.md +15 -0
  161. package/.agent/skills/data-modeler/rules/_template.md +32 -0
  162. package/.agent/skills/data-modeler/rules/database-selection.md +124 -0
  163. package/.agent/skills/data-modeler/rules/engineering-spec.md +479 -0
  164. package/.agent/skills/data-modeler/rules/indexing.md +166 -0
  165. package/.agent/skills/data-modeler/rules/migrations.md +176 -0
  166. package/.agent/skills/data-modeler/rules/optimization.md +161 -0
  167. package/.agent/skills/data-modeler/rules/orm-selection.md +155 -0
  168. package/.agent/skills/data-modeler/rules/schema-design.md +162 -0
  169. package/.agent/skills/data-modeler/scripts/schema_validator.ts +357 -0
  170. package/.agent/skills/debug-pro/AGENTS.md +798 -0
  171. package/.agent/skills/debug-pro/SKILL.md +193 -0
  172. package/.agent/skills/debug-pro/defense-in-depth/SKILL.md +148 -0
  173. package/.agent/skills/debug-pro/root-cause-tracing/SKILL.md +196 -0
  174. package/.agent/skills/debug-pro/root-cause-tracing/find-polluter.sh +63 -0
  175. package/.agent/skills/debug-pro/rules/_sections.md +15 -0
  176. package/.agent/skills/debug-pro/rules/_template.md +32 -0
  177. package/.agent/skills/debug-pro/rules/engineering-spec.md +491 -0
  178. package/.agent/skills/debug-pro/scripts/debug_verifier.ts +148 -0
  179. package/.agent/skills/debug-pro/verification-before-completion/SKILL.md +160 -0
  180. package/.agent/skills/design-system/AGENTS.md +4216 -0
  181. package/.agent/skills/design-system/SKILL.md +186 -0
  182. package/.agent/skills/design-system/rules/_sections.md +65 -0
  183. package/.agent/skills/design-system/rules/_template.md +32 -0
  184. package/.agent/skills/design-system/rules/animation-guide.md +355 -0
  185. package/.agent/skills/design-system/rules/color-system.md +335 -0
  186. package/.agent/skills/design-system/rules/color-systems.md +133 -0
  187. package/.agent/skills/design-system/rules/decision-trees.md +442 -0
  188. package/.agent/skills/design-system/rules/design-extraction.md +152 -0
  189. package/.agent/skills/design-system/rules/engineering-spec.md +484 -0
  190. package/.agent/skills/design-system/rules/motion-design.md +161 -0
  191. package/.agent/skills/design-system/rules/motion-graphics.md +330 -0
  192. package/.agent/skills/design-system/rules/spatial-composition.md +184 -0
  193. package/.agent/skills/design-system/rules/typography-system.md +369 -0
  194. package/.agent/skills/design-system/rules/typography.md +124 -0
  195. package/.agent/skills/design-system/rules/ux-psychology.md +565 -0
  196. package/.agent/skills/design-system/rules/visual-effects.md +407 -0
  197. package/.agent/skills/design-system/scripts/accessibility_checker.ts +292 -0
  198. package/.agent/skills/design-system/scripts/ux_audit.ts +356 -0
  199. package/.agent/skills/doc-templates/AGENTS.md +820 -0
  200. package/.agent/skills/doc-templates/SKILL.md +260 -0
  201. package/.agent/skills/doc-templates/rules/_sections.md +20 -0
  202. package/.agent/skills/doc-templates/rules/_template.md +32 -0
  203. package/.agent/skills/doc-templates/rules/doc.md +355 -0
  204. package/.agent/skills/doc-templates/rules/engineering-spec.md +422 -0
  205. package/.agent/skills/doc-templates/scripts/editor-server.ts +162 -0
  206. package/.agent/skills/doc-templates/scripts/inject_otel.ts +22 -0
  207. package/.agent/skills/doc-templates/scripts/kanban-server.ts +171 -0
  208. package/.agent/skills/doc-templates/scripts/markdown-server.ts +185 -0
  209. package/.agent/skills/e2e-automation/AGENTS.md +882 -0
  210. package/.agent/skills/e2e-automation/SKILL.md +175 -0
  211. package/.agent/skills/e2e-automation/rules/_sections.md +20 -0
  212. package/.agent/skills/e2e-automation/rules/_template.md +32 -0
  213. package/.agent/skills/e2e-automation/rules/aria-snapshot.md +185 -0
  214. package/.agent/skills/e2e-automation/rules/engineering-spec.md +501 -0
  215. package/.agent/skills/e2e-automation/scripts/playwright_runner.ts +208 -0
  216. package/.agent/skills/execution-reporter/AGENTS.md +419 -0
  217. package/.agent/skills/execution-reporter/SKILL.md +152 -0
  218. package/.agent/skills/execution-reporter/rules/_sections.md +15 -0
  219. package/.agent/skills/execution-reporter/rules/_template.md +32 -0
  220. package/.agent/skills/execution-reporter/rules/engineering-spec.md +389 -0
  221. package/.agent/skills/game-development/2d-games/SKILL.md +140 -0
  222. package/.agent/skills/game-development/3d-games/SKILL.md +156 -0
  223. package/.agent/skills/game-development/AGENTS.md +783 -0
  224. package/.agent/skills/game-development/SKILL.md +178 -0
  225. package/.agent/skills/game-development/game-art/SKILL.md +207 -0
  226. package/.agent/skills/game-development/game-audio/SKILL.md +211 -0
  227. package/.agent/skills/game-development/game-design/SKILL.md +151 -0
  228. package/.agent/skills/game-development/mobile-games/SKILL.md +130 -0
  229. package/.agent/skills/game-development/multiplayer/SKILL.md +154 -0
  230. package/.agent/skills/game-development/pc-games/SKILL.md +167 -0
  231. package/.agent/skills/game-development/rules/_sections.md +15 -0
  232. package/.agent/skills/game-development/rules/_template.md +32 -0
  233. package/.agent/skills/game-development/rules/engineering-spec.md +480 -0
  234. package/.agent/skills/game-development/vr-ar/SKILL.md +144 -0
  235. package/.agent/skills/game-development/web-games/SKILL.md +173 -0
  236. package/.agent/skills/git-workflow/AGENTS.md +554 -0
  237. package/.agent/skills/git-workflow/SKILL.md +181 -0
  238. package/.agent/skills/git-workflow/rules/_sections.md +15 -0
  239. package/.agent/skills/git-workflow/rules/_template.md +32 -0
  240. package/.agent/skills/git-workflow/rules/engineering-spec.md +518 -0
  241. package/.agent/skills/gitops/AGENTS.md +921 -0
  242. package/.agent/skills/gitops/SKILL.md +163 -0
  243. package/.agent/skills/gitops/rules/_sections.md +25 -0
  244. package/.agent/skills/gitops/rules/_template.md +32 -0
  245. package/.agent/skills/gitops/rules/argocd-setup.md +148 -0
  246. package/.agent/skills/gitops/rules/engineering-spec.md +450 -0
  247. package/.agent/skills/gitops/rules/sync-policies.md +145 -0
  248. package/.agent/skills/google-adk-python/AGENTS.md +1054 -0
  249. package/.agent/skills/google-adk-python/SKILL.md +168 -0
  250. package/.agent/skills/google-adk-python/rules/_sections.md +25 -0
  251. package/.agent/skills/google-adk-python/rules/_template.md +32 -0
  252. package/.agent/skills/google-adk-python/rules/deployment.md +138 -0
  253. package/.agent/skills/google-adk-python/rules/engineering-spec.md +451 -0
  254. package/.agent/skills/google-adk-python/rules/multi-agent.md +146 -0
  255. package/.agent/skills/google-adk-python/rules/tools.md +131 -0
  256. package/.agent/skills/idea-storm/AGENTS.md +995 -0
  257. package/.agent/skills/idea-storm/SKILL.md +160 -0
  258. package/.agent/skills/idea-storm/rules/_sections.md +25 -0
  259. package/.agent/skills/idea-storm/rules/_template.md +32 -0
  260. package/.agent/skills/idea-storm/rules/architecture-debate.md +122 -0
  261. package/.agent/skills/idea-storm/rules/dynamic-questioning.md +374 -0
  262. package/.agent/skills/idea-storm/rules/engineering-spec.md +466 -0
  263. package/.agent/skills/knowledge-compiler/SKILL.md +320 -0
  264. package/.agent/skills/knowledge-graph/AGENTS.md +762 -0
  265. package/.agent/skills/knowledge-graph/SKILL.md +157 -0
  266. package/.agent/skills/knowledge-graph/rules/_sections.md +15 -0
  267. package/.agent/skills/knowledge-graph/rules/_template.md +32 -0
  268. package/.agent/skills/knowledge-graph/rules/engineering-spec.md +439 -0
  269. package/.agent/skills/knowledge-linter/SKILL.md +217 -0
  270. package/.agent/skills/lifecycle-orchestrator/AGENTS.md +989 -0
  271. package/.agent/skills/lifecycle-orchestrator/SKILL.md +169 -0
  272. package/.agent/skills/lifecycle-orchestrator/rules/_sections.md +15 -0
  273. package/.agent/skills/lifecycle-orchestrator/rules/_template.md +32 -0
  274. package/.agent/skills/lifecycle-orchestrator/rules/engineering-spec.md +525 -0
  275. package/.agent/skills/lifecycle-orchestrator/scripts/state_manager.ts +189 -0
  276. package/.agent/skills/mcp-builder/AGENTS.md +1653 -0
  277. package/.agent/skills/mcp-builder/SKILL.md +166 -0
  278. package/.agent/skills/mcp-builder/rules/_sections.md +40 -0
  279. package/.agent/skills/mcp-builder/rules/_template.md +32 -0
  280. package/.agent/skills/mcp-builder/rules/best-practices.md +157 -0
  281. package/.agent/skills/mcp-builder/rules/design-principles.md +105 -0
  282. package/.agent/skills/mcp-builder/rules/engineering-spec.md +473 -0
  283. package/.agent/skills/mcp-builder/rules/evaluation.md +103 -0
  284. package/.agent/skills/mcp-builder/rules/python-implementation.md +249 -0
  285. package/.agent/skills/mcp-builder/rules/quickstart.md +111 -0
  286. package/.agent/skills/mcp-builder/rules/typescript-implementation.md +280 -0
  287. package/.agent/skills/mcp-management/AGENTS.md +837 -0
  288. package/.agent/skills/mcp-management/SKILL.md +164 -0
  289. package/.agent/skills/mcp-management/rules/_sections.md +25 -0
  290. package/.agent/skills/mcp-management/rules/_template.md +32 -0
  291. package/.agent/skills/mcp-management/rules/cli-usage.md +146 -0
  292. package/.agent/skills/mcp-management/rules/engineering-spec.md +501 -0
  293. package/.agent/skills/mcp-management/rules/protocol.md +159 -0
  294. package/.agent/skills/media-processing/AGENTS.md +479 -0
  295. package/.agent/skills/media-processing/SKILL.md +176 -0
  296. package/.agent/skills/media-processing/rules/_sections.md +15 -0
  297. package/.agent/skills/media-processing/rules/_template.md +32 -0
  298. package/.agent/skills/media-processing/rules/engineering-spec.md +452 -0
  299. package/.agent/skills/media-processing/scripts/convert-video.ts +155 -0
  300. package/.agent/skills/media-processing/scripts/optimize-image.ts +127 -0
  301. package/.agent/skills/mobile-design/AGENTS.md +6531 -0
  302. package/.agent/skills/mobile-design/SKILL.md +165 -0
  303. package/.agent/skills/mobile-design/rules/_sections.md +45 -0
  304. package/.agent/skills/mobile-design/rules/_template.md +32 -0
  305. package/.agent/skills/mobile-design/rules/decision-trees.md +540 -0
  306. package/.agent/skills/mobile-design/rules/engineering-spec.md +467 -0
  307. package/.agent/skills/mobile-design/rules/mobile-backend.md +516 -0
  308. package/.agent/skills/mobile-design/rules/mobile-color-system.md +436 -0
  309. package/.agent/skills/mobile-design/rules/mobile-debugging.md +146 -0
  310. package/.agent/skills/mobile-design/rules/mobile-design-thinking.md +381 -0
  311. package/.agent/skills/mobile-design/rules/mobile-navigation.md +474 -0
  312. package/.agent/skills/mobile-design/rules/mobile-performance.md +783 -0
  313. package/.agent/skills/mobile-design/rules/mobile-testing.md +380 -0
  314. package/.agent/skills/mobile-design/rules/mobile-typography.md +449 -0
  315. package/.agent/skills/mobile-design/rules/platform-android.md +682 -0
  316. package/.agent/skills/mobile-design/rules/platform-ios.md +577 -0
  317. package/.agent/skills/mobile-design/rules/touch-psychology.md +553 -0
  318. package/.agent/skills/mobile-design/scripts/mobile_audit.ts +309 -0
  319. package/.agent/skills/mobile-developer/AGENTS.md +904 -0
  320. package/.agent/skills/mobile-developer/SKILL.md +194 -0
  321. package/.agent/skills/mobile-developer/rules/_sections.md +75 -0
  322. package/.agent/skills/mobile-developer/rules/_template.md +32 -0
  323. package/.agent/skills/mobile-developer/rules/anti-patterns.md +70 -0
  324. package/.agent/skills/mobile-developer/rules/app-store-optimization.md +319 -0
  325. package/.agent/skills/mobile-developer/rules/decision-trees.md +545 -0
  326. package/.agent/skills/mobile-developer/rules/deep-linking.md +441 -0
  327. package/.agent/skills/mobile-developer/rules/engineering-spec.md +477 -0
  328. package/.agent/skills/mobile-developer/rules/flutter.md +475 -0
  329. package/.agent/skills/mobile-developer/rules/mobile-backend.md +516 -0
  330. package/.agent/skills/mobile-developer/rules/mobile-color-system.md +444 -0
  331. package/.agent/skills/mobile-developer/rules/mobile-debugging.md +428 -0
  332. package/.agent/skills/mobile-developer/rules/mobile-design-thinking.md +367 -0
  333. package/.agent/skills/mobile-developer/rules/mobile-navigation.md +483 -0
  334. package/.agent/skills/mobile-developer/rules/mobile-performance.md +778 -0
  335. package/.agent/skills/mobile-developer/rules/mobile-testing.md +382 -0
  336. package/.agent/skills/mobile-developer/rules/mobile-typography.md +457 -0
  337. package/.agent/skills/mobile-developer/rules/native.md +572 -0
  338. package/.agent/skills/mobile-developer/rules/platform-android.md +676 -0
  339. package/.agent/skills/mobile-developer/rules/platform-ios.md +571 -0
  340. package/.agent/skills/mobile-developer/rules/push-notifications.md +599 -0
  341. package/.agent/skills/mobile-developer/rules/react-native.md +422 -0
  342. package/.agent/skills/mobile-developer/rules/touch-psychology.md +547 -0
  343. package/.agent/skills/mobile-developer/scripts/mobile_audit.ts +701 -0
  344. package/.agent/skills/nextjs-pro/AGENTS.md +3932 -0
  345. package/.agent/skills/nextjs-pro/SKILL.md +171 -0
  346. package/.agent/skills/nextjs-pro/rules/_sections.md +50 -0
  347. package/.agent/skills/nextjs-pro/rules/_template.md +32 -0
  348. package/.agent/skills/nextjs-pro/rules/advanced-event-handler-refs.md +59 -0
  349. package/.agent/skills/nextjs-pro/rules/advanced-init-once.md +46 -0
  350. package/.agent/skills/nextjs-pro/rules/advanced-use-latest.md +43 -0
  351. package/.agent/skills/nextjs-pro/rules/async-api-routes.md +42 -0
  352. package/.agent/skills/nextjs-pro/rules/async-defer-await.md +84 -0
  353. package/.agent/skills/nextjs-pro/rules/async-dependencies.md +55 -0
  354. package/.agent/skills/nextjs-pro/rules/async-parallel.md +32 -0
  355. package/.agent/skills/nextjs-pro/rules/async-suspense-boundaries.md +103 -0
  356. package/.agent/skills/nextjs-pro/rules/bundle-barrel-imports.md +63 -0
  357. package/.agent/skills/nextjs-pro/rules/bundle-conditional.md +35 -0
  358. package/.agent/skills/nextjs-pro/rules/bundle-defer-third-party.md +53 -0
  359. package/.agent/skills/nextjs-pro/rules/bundle-dynamic-imports.md +39 -0
  360. package/.agent/skills/nextjs-pro/rules/bundle-preload.md +54 -0
  361. package/.agent/skills/nextjs-pro/rules/client-event-listeners.md +78 -0
  362. package/.agent/skills/nextjs-pro/rules/client-localstorage-schema.md +75 -0
  363. package/.agent/skills/nextjs-pro/rules/client-passive-event-listeners.md +52 -0
  364. package/.agent/skills/nextjs-pro/rules/client-swr-dedup.md +60 -0
  365. package/.agent/skills/nextjs-pro/rules/engineering-spec.md +440 -0
  366. package/.agent/skills/nextjs-pro/rules/js-batch-dom-css.md +111 -0
  367. package/.agent/skills/nextjs-pro/rules/js-cache-function-results.md +84 -0
  368. package/.agent/skills/nextjs-pro/rules/js-cache-property-access.md +32 -0
  369. package/.agent/skills/nextjs-pro/rules/js-cache-storage.md +74 -0
  370. package/.agent/skills/nextjs-pro/rules/js-combine-iterations.md +36 -0
  371. package/.agent/skills/nextjs-pro/rules/js-early-exit.md +54 -0
  372. package/.agent/skills/nextjs-pro/rules/js-hoist-regexp.md +49 -0
  373. package/.agent/skills/nextjs-pro/rules/js-index-maps.md +41 -0
  374. package/.agent/skills/nextjs-pro/rules/js-length-check-first.md +53 -0
  375. package/.agent/skills/nextjs-pro/rules/js-min-max-loop.md +86 -0
  376. package/.agent/skills/nextjs-pro/rules/js-set-map-lookups.md +28 -0
  377. package/.agent/skills/nextjs-pro/rules/js-tosorted-immutable.md +61 -0
  378. package/.agent/skills/nextjs-pro/rules/rendering-activity.md +30 -0
  379. package/.agent/skills/nextjs-pro/rules/rendering-animate-svg-wrapper.md +51 -0
  380. package/.agent/skills/nextjs-pro/rules/rendering-conditional-render.md +44 -0
  381. package/.agent/skills/nextjs-pro/rules/rendering-content-visibility.md +42 -0
  382. package/.agent/skills/nextjs-pro/rules/rendering-hoist-jsx.md +50 -0
  383. package/.agent/skills/nextjs-pro/rules/rendering-hydration-no-flicker.md +86 -0
  384. package/.agent/skills/nextjs-pro/rules/rendering-hydration-suppress-warning.md +34 -0
  385. package/.agent/skills/nextjs-pro/rules/rendering-svg-precision.md +32 -0
  386. package/.agent/skills/nextjs-pro/rules/rendering-usetransition-loading.md +79 -0
  387. package/.agent/skills/nextjs-pro/rules/rerender-defer-reads.md +43 -0
  388. package/.agent/skills/nextjs-pro/rules/rerender-dependencies.md +49 -0
  389. package/.agent/skills/nextjs-pro/rules/rerender-derived-state-no-effect.md +44 -0
  390. package/.agent/skills/nextjs-pro/rules/rerender-derived-state.md +33 -0
  391. package/.agent/skills/nextjs-pro/rules/rerender-functional-setstate.md +78 -0
  392. package/.agent/skills/nextjs-pro/rules/rerender-lazy-state-init.md +62 -0
  393. package/.agent/skills/nextjs-pro/rules/rerender-memo-with-default-value.md +42 -0
  394. package/.agent/skills/nextjs-pro/rules/rerender-memo.md +48 -0
  395. package/.agent/skills/nextjs-pro/rules/rerender-move-effect-to-event.md +49 -0
  396. package/.agent/skills/nextjs-pro/rules/rerender-simple-expression-in-memo.md +39 -0
  397. package/.agent/skills/nextjs-pro/rules/rerender-transitions.md +44 -0
  398. package/.agent/skills/nextjs-pro/rules/rerender-use-ref-transient-values.md +77 -0
  399. package/.agent/skills/nextjs-pro/rules/schema.json +34 -0
  400. package/.agent/skills/nextjs-pro/rules/server-after-nonblocking.md +77 -0
  401. package/.agent/skills/nextjs-pro/rules/server-auth-actions.md +100 -0
  402. package/.agent/skills/nextjs-pro/rules/server-cache-lru.md +45 -0
  403. package/.agent/skills/nextjs-pro/rules/server-cache-react.md +80 -0
  404. package/.agent/skills/nextjs-pro/rules/server-dedup-props.md +69 -0
  405. package/.agent/skills/nextjs-pro/rules/server-parallel-fetching.md +87 -0
  406. package/.agent/skills/nextjs-pro/rules/server-serialization.md +42 -0
  407. package/.agent/skills/nodejs-pro/AGENTS.md +866 -0
  408. package/.agent/skills/nodejs-pro/SKILL.md +172 -0
  409. package/.agent/skills/nodejs-pro/rules/_sections.md +50 -0
  410. package/.agent/skills/nodejs-pro/rules/_template.md +32 -0
  411. package/.agent/skills/nodejs-pro/rules/architecture-patterns.md +229 -0
  412. package/.agent/skills/nodejs-pro/rules/async-patterns.md +246 -0
  413. package/.agent/skills/nodejs-pro/rules/engineering-spec.md +438 -0
  414. package/.agent/skills/nodejs-pro/rules/error-handling.md +257 -0
  415. package/.agent/skills/nodejs-pro/rules/framework-selection.md +220 -0
  416. package/.agent/skills/nodejs-pro/rules/runtime-modules.md +176 -0
  417. package/.agent/skills/nodejs-pro/rules/testing-strategy.md +266 -0
  418. package/.agent/skills/nodejs-pro/rules/validation-security.md +205 -0
  419. package/.agent/skills/observability/AGENTS.md +607 -0
  420. package/.agent/skills/observability/SKILL.md +178 -0
  421. package/.agent/skills/observability/rules/_sections.md +15 -0
  422. package/.agent/skills/observability/rules/_template.md +32 -0
  423. package/.agent/skills/observability/rules/engineering-spec.md +440 -0
  424. package/.agent/skills/offensive-sec/AGENTS.md +849 -0
  425. package/.agent/skills/offensive-sec/SKILL.md +191 -0
  426. package/.agent/skills/offensive-sec/rules/_sections.md +15 -0
  427. package/.agent/skills/offensive-sec/rules/_template.md +32 -0
  428. package/.agent/skills/offensive-sec/rules/engineering-spec.md +470 -0
  429. package/.agent/skills/perf-optimizer/AGENTS.md +870 -0
  430. package/.agent/skills/perf-optimizer/SKILL.md +189 -0
  431. package/.agent/skills/perf-optimizer/rules/_sections.md +15 -0
  432. package/.agent/skills/perf-optimizer/rules/_template.md +32 -0
  433. package/.agent/skills/perf-optimizer/rules/backend-patterns.md +312 -0
  434. package/.agent/skills/perf-optimizer/rules/engineering-spec.md +428 -0
  435. package/.agent/skills/perf-optimizer/scripts/lighthouse_audit.ts +201 -0
  436. package/.agent/skills/problem-checker/AGENTS.md +519 -0
  437. package/.agent/skills/problem-checker/SKILL.md +189 -0
  438. package/.agent/skills/problem-checker/rules/_sections.md +15 -0
  439. package/.agent/skills/problem-checker/rules/_template.md +32 -0
  440. package/.agent/skills/problem-checker/rules/engineering-spec.md +483 -0
  441. package/.agent/skills/problem-checker/scripts/check_problems.ts +396 -0
  442. package/.agent/skills/project-planner/AGENTS.md +2698 -0
  443. package/.agent/skills/project-planner/SKILL.md +166 -0
  444. package/.agent/skills/project-planner/rules/_sections.md +15 -0
  445. package/.agent/skills/project-planner/rules/_template.md +32 -0
  446. package/.agent/skills/project-planner/rules/engineering-spec.md +420 -0
  447. package/.agent/skills/python-pro/AGENTS.md +1871 -0
  448. package/.agent/skills/python-pro/SKILL.md +182 -0
  449. package/.agent/skills/python-pro/rules/_sections.md +50 -0
  450. package/.agent/skills/python-pro/rules/_template.md +32 -0
  451. package/.agent/skills/python-pro/rules/async-patterns.md +168 -0
  452. package/.agent/skills/python-pro/rules/django-patterns.md +194 -0
  453. package/.agent/skills/python-pro/rules/engineering-spec.md +442 -0
  454. package/.agent/skills/python-pro/rules/fastapi-patterns.md +179 -0
  455. package/.agent/skills/python-pro/rules/framework-selection.md +167 -0
  456. package/.agent/skills/python-pro/rules/project-structure.md +181 -0
  457. package/.agent/skills/python-pro/rules/testing-patterns.md +212 -0
  458. package/.agent/skills/python-pro/rules/type-hints.md +159 -0
  459. package/.agent/skills/react-pro/AGENTS.md +963 -0
  460. package/.agent/skills/react-pro/SKILL.md +232 -0
  461. package/.agent/skills/react-pro/rules/_sections.md +40 -0
  462. package/.agent/skills/react-pro/rules/_template.md +32 -0
  463. package/.agent/skills/react-pro/rules/component-patterns.md +145 -0
  464. package/.agent/skills/react-pro/rules/composition-compound.md +82 -0
  465. package/.agent/skills/react-pro/rules/data-fetching.md +133 -0
  466. package/.agent/skills/react-pro/rules/engineering-spec.md +453 -0
  467. package/.agent/skills/react-pro/rules/error-boundary.md +61 -0
  468. package/.agent/skills/react-pro/rules/file-organization.md +158 -0
  469. package/.agent/skills/react-pro/rules/hooks-custom.md +61 -0
  470. package/.agent/skills/react-pro/rules/mui-styling.md +138 -0
  471. package/.agent/skills/react-pro/rules/patterns.md +24 -0
  472. package/.agent/skills/react-pro/rules/performance-optimization.md +65 -0
  473. package/.agent/skills/react-pro/rules/performance.md +137 -0
  474. package/.agent/skills/react-pro/rules/react19-hooks.md +85 -0
  475. package/.agent/skills/react-pro/rules/state-management.md +90 -0
  476. package/.agent/skills/react-pro/rules/testing-patterns.md +52 -0
  477. package/.agent/skills/registry.json +1251 -0
  478. package/.agent/skills/security-scanner/AGENTS.md +851 -0
  479. package/.agent/skills/security-scanner/SKILL.md +182 -0
  480. package/.agent/skills/security-scanner/rules/_sections.md +15 -0
  481. package/.agent/skills/security-scanner/rules/_template.md +32 -0
  482. package/.agent/skills/security-scanner/rules/auth-patterns.md +281 -0
  483. package/.agent/skills/security-scanner/rules/checklists.md +186 -0
  484. package/.agent/skills/security-scanner/rules/engineering-spec.md +440 -0
  485. package/.agent/skills/security-scanner/scripts/security_scan.ts +513 -0
  486. package/.agent/skills/seo-optimizer/AGENTS.md +839 -0
  487. package/.agent/skills/seo-optimizer/SKILL.md +180 -0
  488. package/.agent/skills/seo-optimizer/rules/_sections.md +15 -0
  489. package/.agent/skills/seo-optimizer/rules/_template.md +32 -0
  490. package/.agent/skills/seo-optimizer/rules/engineering-spec.md +433 -0
  491. package/.agent/skills/seo-optimizer/scripts/geo_checker.ts +109 -0
  492. package/.agent/skills/seo-optimizer/scripts/seo_checker.ts +308 -0
  493. package/.agent/skills/server-ops/AGENTS.md +643 -0
  494. package/.agent/skills/server-ops/SKILL.md +194 -0
  495. package/.agent/skills/server-ops/rules/_sections.md +15 -0
  496. package/.agent/skills/server-ops/rules/_template.md +32 -0
  497. package/.agent/skills/server-ops/rules/engineering-spec.md +450 -0
  498. package/.agent/skills/shell-script/AGENTS.md +499 -0
  499. package/.agent/skills/shell-script/SKILL.md +205 -0
  500. package/.agent/skills/shell-script/rules/_sections.md +15 -0
  501. package/.agent/skills/shell-script/rules/_template.md +32 -0
  502. package/.agent/skills/shell-script/rules/engineering-spec.md +463 -0
  503. package/.agent/skills/skill-generator/SKILL.md +147 -0
  504. package/.agent/skills/smart-router/SKILL.md +95 -0
  505. package/.agent/skills/studio/AGENTS.md +636 -0
  506. package/.agent/skills/studio/SKILL.md +178 -0
  507. package/.agent/skills/studio/data/charts.csv +26 -0
  508. package/.agent/skills/studio/data/colors.csv +97 -0
  509. package/.agent/skills/studio/data/icons.csv +101 -0
  510. package/.agent/skills/studio/data/landing.csv +31 -0
  511. package/.agent/skills/studio/data/products.csv +97 -0
  512. package/.agent/skills/studio/data/prompts.csv +24 -0
  513. package/.agent/skills/studio/data/react-performance.csv +45 -0
  514. package/.agent/skills/studio/data/stacks/flutter.csv +52 -0
  515. package/.agent/skills/studio/data/stacks/html-tailwind.csv +56 -0
  516. package/.agent/skills/studio/data/stacks/jetpack-compose.csv +53 -0
  517. package/.agent/skills/studio/data/stacks/nextjs.csv +53 -0
  518. package/.agent/skills/studio/data/stacks/nuxt-ui.csv +51 -0
  519. package/.agent/skills/studio/data/stacks/nuxtjs.csv +59 -0
  520. package/.agent/skills/studio/data/stacks/react-native.csv +52 -0
  521. package/.agent/skills/studio/data/stacks/react.csv +54 -0
  522. package/.agent/skills/studio/data/stacks/shadcn.csv +61 -0
  523. package/.agent/skills/studio/data/stacks/svelte.csv +54 -0
  524. package/.agent/skills/studio/data/stacks/swiftui.csv +51 -0
  525. package/.agent/skills/studio/data/stacks/vue.csv +50 -0
  526. package/.agent/skills/studio/data/styles.csv +59 -0
  527. package/.agent/skills/studio/data/typography.csv +58 -0
  528. package/.agent/skills/studio/data/ui-reasoning.csv +101 -0
  529. package/.agent/skills/studio/data/ux-guidelines.csv +100 -0
  530. package/.agent/skills/studio/data/web-interface.csv +31 -0
  531. package/.agent/skills/studio/rules/_sections.md +15 -0
  532. package/.agent/skills/studio/rules/_template.md +32 -0
  533. package/.agent/skills/studio/rules/engineering-spec.md +455 -0
  534. package/.agent/skills/studio/scripts/core.ts +345 -0
  535. package/.agent/skills/studio/scripts/design_system.ts +953 -0
  536. package/.agent/skills/studio/scripts/search.ts +197 -0
  537. package/.agent/skills/studio/scripts/types.ts +147 -0
  538. package/.agent/skills/studio/scripts/utils/component-specs.ts +154 -0
  539. package/.agent/skills/studio/scripts/utils/config-loader.ts +165 -0
  540. package/.agent/skills/studio/scripts/utils/css-templates.ts +169 -0
  541. package/.agent/skills/studio/scripts/utils/css-validator.ts +95 -0
  542. package/.agent/skills/studio/scripts/utils/csv-loader.ts +52 -0
  543. package/.agent/skills/studio/scripts/utils/intelligent-overrides.ts +129 -0
  544. package/.agent/skills/studio/scripts/utils/page-override-formatter.ts +143 -0
  545. package/.agent/skills/studio/scripts/utils/page-type-detector.ts +124 -0
  546. package/.agent/skills/studio/scripts/utils/search-cache.ts +165 -0
  547. package/.agent/skills/studio/scripts/utils/text-utils.ts +44 -0
  548. package/.agent/skills/system-design/AGENTS.md +597 -0
  549. package/.agent/skills/system-design/SKILL.md +153 -0
  550. package/.agent/skills/system-design/rules/_sections.md +15 -0
  551. package/.agent/skills/system-design/rules/_template.md +32 -0
  552. package/.agent/skills/system-design/rules/context-discovery.md +117 -0
  553. package/.agent/skills/system-design/rules/engineering-spec.md +437 -0
  554. package/.agent/skills/system-design/rules/examples.md +180 -0
  555. package/.agent/skills/system-design/rules/pattern-selection.md +130 -0
  556. package/.agent/skills/system-design/rules/patterns-reference.md +110 -0
  557. package/.agent/skills/system-design/rules/trade-off-analysis.md +169 -0
  558. package/.agent/skills/tailwind-kit/AGENTS.md +1135 -0
  559. package/.agent/skills/tailwind-kit/SKILL.md +171 -0
  560. package/.agent/skills/tailwind-kit/rules/_sections.md +20 -0
  561. package/.agent/skills/tailwind-kit/rules/_template.md +32 -0
  562. package/.agent/skills/tailwind-kit/rules/components.md +232 -0
  563. package/.agent/skills/tailwind-kit/rules/engineering-spec.md +435 -0
  564. package/.agent/skills/tailwind-kit/rules/responsive.md +221 -0
  565. package/.agent/skills/tailwind-kit/rules/v4-config.md +72 -0
  566. package/.agent/skills/test-architect/AGENTS.md +851 -0
  567. package/.agent/skills/test-architect/SKILL.md +176 -0
  568. package/.agent/skills/test-architect/rules/_sections.md +15 -0
  569. package/.agent/skills/test-architect/rules/_template.md +32 -0
  570. package/.agent/skills/test-architect/rules/engineering-spec.md +434 -0
  571. package/.agent/skills/test-architect/scripts/test_runner.ts +265 -0
  572. package/.agent/skills/typescript-expert/AGENTS.md +1045 -0
  573. package/.agent/skills/typescript-expert/SKILL.md +200 -0
  574. package/.agent/skills/typescript-expert/rules/_sections.md +20 -0
  575. package/.agent/skills/typescript-expert/rules/_template.md +32 -0
  576. package/.agent/skills/typescript-expert/rules/engineering-spec.md +433 -0
  577. package/.agent/skills/typescript-expert/rules/tsconfig-strict.json +92 -0
  578. package/.agent/skills/typescript-expert/rules/typescript-cheatsheet.md +407 -0
  579. package/.agent/skills/typescript-expert/rules/utility-types.ts +264 -0
  580. package/.agent/skills/typescript-expert/scripts/ts_diagnostic.ts +321 -0
  581. package/.agent/skills/vercel-deploy/AGENTS.md +490 -0
  582. package/.agent/skills/vercel-deploy/SKILL.md +175 -0
  583. package/.agent/skills/vercel-deploy/rules/_sections.md +15 -0
  584. package/.agent/skills/vercel-deploy/rules/_template.md +32 -0
  585. package/.agent/skills/vercel-deploy/rules/engineering-spec.md +463 -0
  586. package/.agent/skills/vercel-deploy/scripts/deploy.sh +310 -0
  587. package/.agent/workflows/api.md +377 -0
  588. package/.agent/workflows/autopilot.md +344 -0
  589. package/.agent/workflows/build.md +338 -0
  590. package/.agent/workflows/chronicle.md +279 -0
  591. package/.agent/workflows/cook.md +217 -0
  592. package/.agent/workflows/diagnose.md +302 -0
  593. package/.agent/workflows/fix.md +253 -0
  594. package/.agent/workflows/game.md +329 -0
  595. package/.agent/workflows/inspect.md +276 -0
  596. package/.agent/workflows/knowledge.md +212 -0
  597. package/.agent/workflows/launch.md +345 -0
  598. package/.agent/workflows/mobile.md +354 -0
  599. package/.agent/workflows/monitor.md +239 -0
  600. package/.agent/workflows/optimize.md +269 -0
  601. package/.agent/workflows/plan.md +278 -0
  602. package/.agent/workflows/stage.md +286 -0
  603. package/.agent/workflows/studio.md +276 -0
  604. package/.agent/workflows/think.md +262 -0
  605. package/.agent/workflows/validate.md +289 -0
  606. package/.agentignore +161 -0
  607. package/.gitattributes +16 -0
  608. package/CHANGELOG.md +198 -0
  609. package/LICENSE +40 -0
  610. package/README.md +173 -0
  611. package/docs/SKILL_DESIGN_GUIDE.md +561 -0
  612. package/docs/The-Complete-Guide-to-Building-Skills-for-Claude.md +1207 -0
  613. package/docs/WORKFLOW_DESIGN_GUIDE.md +325 -0
  614. package/package.json +33 -0
  615. package/tsconfig.json +28 -0
@@ -0,0 +1,3932 @@
1
+ # nextjs-pro
2
+
3
+ **Version 1.0.0**
4
+ Engineering
5
+ March 2026
6
+
7
+ > **Note:**
8
+ > This document is for agents and LLMs to follow when working on nextjs-pro domain.
9
+ > Optimized for automation and consistency by AI-assisted workflows.
10
+
11
+ ---
12
+
13
+ # Next.js Pro — App Router & React Performance
14
+
15
+ > Server-first. 3 data strategies. 4 route files. 57 rules. 8 categories. CWV targets.
16
+
17
+ **Philosophy:** Performance is a feature. Waterfalls are the enemy. Server first.
18
+
19
+ ---
20
+
21
+ ## When to Use
22
+
23
+ | Situation | Action |
24
+ |-----------|--------|
25
+ | Building React frontend | Use App Router patterns |
26
+ | Server vs Client decision | Use component decision tree |
27
+ | Data fetching strategy | Route by volatility |
28
+ | Performance patterns | Read rules/ by category |
29
+
30
+ ---
31
+
32
+ ## System Boundaries
33
+
34
+ | Owned by This Skill | NOT Owned |
35
+ |---------------------|-----------|
36
+ | Server/Client component decision | React architecture (→ react-architect) |
37
+ | Data fetching strategy (3 options) | CSS/styling (→ tailwind-kit) |
38
+ | Routing conventions (4 files) | Design system (→ design-system) |
39
+ | 57 performance rules (8 categories) | Performance profiling (→ perf-optimizer) |
40
+
41
+ **Expert decision skill:** Produces patterns and rule references. Does not write code.
42
+
43
+ ---
44
+
45
+ ## Server vs Client Decision (Binary)
46
+
47
+ ```
48
+ Need useState / useEffect / event handlers?
49
+ ├── YES → 'use client'
50
+ └── NO → Server Component (default, no directive)
51
+ ```
52
+
53
+ | Type | Use For |
54
+ |------|---------|
55
+ | **Server** | Data fetching, layouts, static content |
56
+ | **Client** | Forms, buttons, interactive UI |
57
+
58
+ ---
59
+
60
+ ## Data Fetching (3 Strategies — Fixed)
61
+
62
+ | Volatility | Strategy | Cache Config | Use Case |
63
+ |-----------|----------|-------------|----------|
64
+ | Static | Default | `cache: 'force-cache'` | Content pages |
65
+ | Periodic | ISR | `revalidate: 60` | Dynamic but cacheable |
66
+ | Real-time | Dynamic | `cache: 'no-store'` | Live data |
67
+
68
+ ---
69
+
70
+ ## Route Conventions (4 Files — Fixed)
71
+
72
+ | File | Purpose | Required |
73
+ |------|---------|----------|
74
+ | `page.tsx` | Route UI | Yes |
75
+ | `layout.tsx` | Shared layout | Yes (root) |
76
+ | `loading.tsx` | Loading state (Suspense) | Recommended |
77
+ | `error.tsx` | Error boundary | Recommended |
78
+
79
+ ---
80
+
81
+ ## Core Web Vitals Targets (Fixed)
82
+
83
+ | Metric | Target |
84
+ |--------|--------|
85
+ | LCP | < 2,500 ms |
86
+ | INP | < 200 ms |
87
+ | CLS | < 0.1 |
88
+
89
+ ---
90
+
91
+ ## Error Taxonomy
92
+
93
+ | Code | Recoverable | Trigger |
94
+ |------|-------------|---------|
95
+ | `ERR_INVALID_REQUEST_TYPE` | No | Request type not supported |
96
+ | `ERR_UNKNOWN_CATEGORY` | Yes | Rule category not one of 8 |
97
+ | `ERR_INVALID_VOLATILITY` | Yes | Data volatility not recognized |
98
+
99
+ **Zero internal retries.** Deterministic; same context = same pattern.
100
+
101
+ ---
102
+
103
+ ## Anti-Patterns
104
+
105
+ | ❌ Don't | ✅ Do |
106
+ |---------|-------|
107
+ | `'use client'` everywhere | Server Component by default |
108
+ | Fetch data in Client Components | Fetch in Server Components |
109
+ | Barrel imports (`index.js`) | Direct imports |
110
+ | Nested awaits (waterfall) | `Promise.all()` (parallel) |
111
+ | Skip loading/error states | Use loading.tsx + error.tsx |
112
+
113
+ ---
114
+
115
+ ## 📑 Content Map
116
+
117
+ | Category | Files | Focus | When to Read |
118
+ |----------|-------|-------|--------------|
119
+ | `rules/async-*.md` | 5 | Waterfalls, parallel fetch | Data fetching |
120
+ | `rules/bundle-*.md` | 5 | Tree-shaking, lazy loading | Bundle size |
121
+ | `rules/server-*.md` | 7 | RSC, caching, actions | Server Components |
122
+ | `rules/client-*.md` | 4 | Events, SWR, localStorage | Client Components |
123
+ | `rules/rendering-*.md` | 9 | Hydration, transitions | Rendering issues |
124
+ | `rules/rerender-*.md` | 12 | Memo, state, effects | Re-render prevention |
125
+ | `rules/js-*.md` | 12 | Micro-patterns | JS performance |
126
+ | `rules/advanced-*.md` | 3 | Event refs, init-once, useLatest | Advanced patterns |
127
+ | `rules/_*.md, schema.json` | 3 | Section catalog, template, schema | Rule authoring |
128
+ | [engineering-spec.md](rules/engineering-spec.md) | 1 | Full engineering spec | Architecture review |
129
+
130
+ **Selective reading:** Read ONLY the category relevant to current task.
131
+
132
+ ---
133
+
134
+ ## 🔗 Related
135
+
136
+ | Item | Type | Purpose |
137
+ |------|------|---------|
138
+ | `react-architect` | Skill | React patterns |
139
+ | `tailwind-kit` | Skill | Styling |
140
+ | `perf-optimizer` | Skill | Performance profiling |
141
+
142
+ ---
143
+
144
+ ## Agent Contract
145
+
146
+ ### Output Schema (JSON)
147
+
148
+ Agents executing `nextjs-pro` patterns must output this execution metadata:
149
+
150
+ ```json
151
+ {
152
+ "agent": "nextjs-pro",
153
+ "status": "success | failure | escalate",
154
+ "result": {
155
+ "rendering_strategy": "ssr | ssg | isr | csr",
156
+ "data_volatility": "static | periodic | dynamic",
157
+ "cwv_passed": true,
158
+ "code_quality": { "problem_checker_run": true, "errors_fixed": 0 }
159
+ },
160
+ "artifacts": ["path/to/page.tsx", "path/to/layout.tsx"]
161
+ }
162
+ ```
163
+
164
+ ### Audit Logging (OpenTelemetry Mapped)
165
+
166
+ ```json
167
+ {
168
+ "traceId": "uuid",
169
+ "spanId": "uuid",
170
+ "parentSpanId": "uuid | null",
171
+ "name": "nextjs-pro.execution",
172
+ "kind": "AGENT",
173
+ "events": [
174
+ { "name": "start", "timestamp": "ISO8601" },
175
+ { "name": "architecture_decision", "timestamp": "ISO8601", "attributes": {"rendering": "ssr"} },
176
+ { "name": "build_verification", "timestamp": "ISO8601", "attributes": {"build_status": "success"} }
177
+ ],
178
+ "status": {
179
+ "code": "OK | ERROR",
180
+ "description": "string | null"
181
+ }
182
+ }
183
+ ```
184
+
185
+ ---
186
+
187
+ ## Detailed Rules
188
+
189
+
190
+ ---
191
+
192
+ ### Rule: advanced-event-handler-refs
193
+
194
+ ---
195
+ title: Store Event Handlers in Refs
196
+ impact: LOW
197
+ impactDescription: stable subscriptions
198
+ tags: advanced, hooks, refs, event-handlers, optimization
199
+ ---
200
+
201
+ ## Store Event Handlers in Refs
202
+
203
+ Store callbacks in refs when used in effects that shouldn't re-subscribe on callback changes.
204
+
205
+ **Incorrect (re-subscribes on every render):**
206
+
207
+ ```tsx
208
+ function useWindowEvent(event: string, handler: (e) => void) {
209
+ useEffect(() => {
210
+ window.addEventListener(event, handler)
211
+ return () => window.removeEventListener(event, handler)
212
+ }, [event, handler])
213
+ }
214
+ ```
215
+
216
+ **Correct (stable subscription):**
217
+
218
+ ```tsx
219
+ function useWindowEvent(event: string, handler: (e) => void) {
220
+ const handlerRef = useRef(handler)
221
+ useEffect(() => {
222
+ handlerRef.current = handler
223
+ }, [handler])
224
+
225
+ useEffect(() => {
226
+ const listener = (e) => handlerRef.current(e)
227
+ window.addEventListener(event, listener)
228
+ return () => window.removeEventListener(event, listener)
229
+ }, [event])
230
+ }
231
+ ```
232
+
233
+ **Alternative: use `useEffectEvent` if you're on latest React:**
234
+
235
+ ```tsx
236
+ import { useEffectEvent } from 'react'
237
+
238
+ function useWindowEvent(event: string, handler: (e) => void) {
239
+ const onEvent = useEffectEvent(handler)
240
+
241
+ useEffect(() => {
242
+ window.addEventListener(event, onEvent)
243
+ return () => window.removeEventListener(event, onEvent)
244
+ }, [event])
245
+ }
246
+ ```
247
+
248
+ `useEffectEvent` provides a cleaner API for the same pattern: it creates a stable function reference that always calls the latest version of the handler.
249
+
250
+ ---
251
+
252
+ ### Rule: advanced-init-once
253
+
254
+ ---
255
+ title: Initialize App Once, Not Per Mount
256
+ impact: LOW-MEDIUM
257
+ impactDescription: avoids duplicate init in development
258
+ tags: initialization, useEffect, app-startup, side-effects
259
+ ---
260
+
261
+ ## Initialize App Once, Not Per Mount
262
+
263
+ Do not put app-wide initialization that must run once per app load inside `useEffect([])` of a component. Components can remount and effects will re-run. Use a module-level guard or top-level init in the entry module instead.
264
+
265
+ **Incorrect (runs twice in dev, re-runs on remount):**
266
+
267
+ ```tsx
268
+ function Comp() {
269
+ useEffect(() => {
270
+ loadFromStorage()
271
+ checkAuthToken()
272
+ }, [])
273
+
274
+ // ...
275
+ }
276
+ ```
277
+
278
+ **Correct (once per app load):**
279
+
280
+ ```tsx
281
+ let didInit = false
282
+
283
+ function Comp() {
284
+ useEffect(() => {
285
+ if (didInit) return
286
+ didInit = true
287
+ loadFromStorage()
288
+ checkAuthToken()
289
+ }, [])
290
+
291
+ // ...
292
+ }
293
+ ```
294
+
295
+ Reference: [Initializing the application](https://react.dev/learn/you-might-not-need-an-effect#initializing-the-application)
296
+
297
+ ---
298
+
299
+ ### Rule: advanced-use-latest
300
+
301
+ ---
302
+ title: useEffectEvent for Stable Callback Refs
303
+ impact: LOW
304
+ impactDescription: prevents effect re-runs
305
+ tags: advanced, hooks, useEffectEvent, refs, optimization
306
+ ---
307
+
308
+ ## useEffectEvent for Stable Callback Refs
309
+
310
+ Access latest values in callbacks without adding them to dependency arrays. Prevents effect re-runs while avoiding stale closures.
311
+
312
+ **Incorrect (effect re-runs on every callback change):**
313
+
314
+ ```tsx
315
+ function SearchInput({ onSearch }: { onSearch: (q: string) => void }) {
316
+ const [query, setQuery] = useState('')
317
+
318
+ useEffect(() => {
319
+ const timeout = setTimeout(() => onSearch(query), 300)
320
+ return () => clearTimeout(timeout)
321
+ }, [query, onSearch])
322
+ }
323
+ ```
324
+
325
+ **Correct (using React's useEffectEvent):**
326
+
327
+ ```tsx
328
+ import { useEffectEvent } from 'react';
329
+
330
+ function SearchInput({ onSearch }: { onSearch: (q: string) => void }) {
331
+ const [query, setQuery] = useState('')
332
+ const onSearchEvent = useEffectEvent(onSearch)
333
+
334
+ useEffect(() => {
335
+ const timeout = setTimeout(() => onSearchEvent(query), 300)
336
+ return () => clearTimeout(timeout)
337
+ }, [query])
338
+ }
339
+ ```
340
+
341
+ ---
342
+
343
+ ### Rule: async-api-routes
344
+
345
+ ---
346
+ title: Prevent Waterfall Chains in API Routes
347
+ impact: CRITICAL
348
+ impactDescription: 2-10× improvement
349
+ tags: api-routes, server-actions, waterfalls, parallelization
350
+ ---
351
+
352
+ ## Prevent Waterfall Chains in API Routes
353
+
354
+ In API routes and Server Actions, start independent operations immediately, even if you don't await them yet.
355
+
356
+ **Incorrect (config waits for auth, data waits for both):**
357
+
358
+ ```typescript
359
+ export async function GET(request: Request) {
360
+ const session = await auth()
361
+ const config = await fetchConfig()
362
+ const data = await fetchData(session.user.id)
363
+ return Response.json({ data, config })
364
+ }
365
+ ```
366
+
367
+ **Correct (auth and config start immediately):**
368
+
369
+ ```typescript
370
+ export async function GET(request: Request) {
371
+ const sessionPromise = auth()
372
+ const configPromise = fetchConfig()
373
+ const session = await sessionPromise
374
+ const [config, data] = await Promise.all([
375
+ configPromise,
376
+ fetchData(session.user.id)
377
+ ])
378
+ return Response.json({ data, config })
379
+ }
380
+ ```
381
+
382
+ For operations with more complex dependency chains, use `better-all` to automatically maximize parallelism (see Dependency-Based Parallelization).
383
+
384
+ ---
385
+
386
+ ### Rule: async-defer-await
387
+
388
+ ---
389
+ title: Defer Await Until Needed
390
+ impact: HIGH
391
+ impactDescription: avoids blocking unused code paths
392
+ tags: async, await, conditional, optimization
393
+ ---
394
+
395
+ ## Defer Await Until Needed
396
+
397
+ Move `await` operations into the branches where they're actually used to avoid blocking code paths that don't need them.
398
+
399
+ **Incorrect (blocks both branches):**
400
+
401
+ ```typescript
402
+ async function handleRequest(userId: string, skipProcessing: boolean) {
403
+ const userData = await fetchUserData(userId)
404
+
405
+ if (skipProcessing) {
406
+ // Returns immediately but still waited for userData
407
+ return { skipped: true }
408
+ }
409
+
410
+ // Only this branch uses userData
411
+ return processUserData(userData)
412
+ }
413
+ ```
414
+
415
+ **Correct (only blocks when needed):**
416
+
417
+ ```typescript
418
+ async function handleRequest(userId: string, skipProcessing: boolean) {
419
+ if (skipProcessing) {
420
+ // Returns immediately without waiting
421
+ return { skipped: true }
422
+ }
423
+
424
+ // Fetch only when needed
425
+ const userData = await fetchUserData(userId)
426
+ return processUserData(userData)
427
+ }
428
+ ```
429
+
430
+ **Another example (early return optimization):**
431
+
432
+ ```typescript
433
+ // Incorrect: always fetches permissions
434
+ async function updateResource(resourceId: string, userId: string) {
435
+ const permissions = await fetchPermissions(userId)
436
+ const resource = await getResource(resourceId)
437
+
438
+ if (!resource) {
439
+ return { error: 'Not found' }
440
+ }
441
+
442
+ if (!permissions.canEdit) {
443
+ return { error: 'Forbidden' }
444
+ }
445
+
446
+ return await updateResourceData(resource, permissions)
447
+ }
448
+
449
+ // Correct: fetches only when needed
450
+ async function updateResource(resourceId: string, userId: string) {
451
+ const resource = await getResource(resourceId)
452
+
453
+ if (!resource) {
454
+ return { error: 'Not found' }
455
+ }
456
+
457
+ const permissions = await fetchPermissions(userId)
458
+
459
+ if (!permissions.canEdit) {
460
+ return { error: 'Forbidden' }
461
+ }
462
+
463
+ return await updateResourceData(resource, permissions)
464
+ }
465
+ ```
466
+
467
+ This optimization is especially valuable when the skipped branch is frequently taken, or when the deferred operation is expensive.
468
+
469
+ ---
470
+
471
+ ### Rule: async-dependencies
472
+
473
+ ---
474
+ title: Dependency-Based Parallelization
475
+ impact: CRITICAL
476
+ impactDescription: 2-10× improvement
477
+ tags: async, parallelization, dependencies, better-all
478
+ ---
479
+
480
+ ## Dependency-Based Parallelization
481
+
482
+ For operations with partial dependencies, use `better-all` to maximize parallelism. It automatically starts each task at the earliest possible moment.
483
+
484
+ **Incorrect (profile waits for config unnecessarily):**
485
+
486
+ ```typescript
487
+ const [user, config] = await Promise.all([
488
+ fetchUser(),
489
+ fetchConfig()
490
+ ])
491
+ const profile = await fetchProfile(user.id)
492
+ ```
493
+
494
+ **Correct (config and profile run in parallel):**
495
+
496
+ ```typescript
497
+ import { all } from 'better-all'
498
+
499
+ const { user, config, profile } = await all({
500
+ async user() { return fetchUser() },
501
+ async config() { return fetchConfig() },
502
+ async profile() {
503
+ return fetchProfile((await this.$.user).id)
504
+ }
505
+ })
506
+ ```
507
+
508
+ **Alternative without extra dependencies:**
509
+
510
+ We can also create all the promises first, and do `Promise.all()` at the end.
511
+
512
+ ```typescript
513
+ const userPromise = fetchUser()
514
+ const profilePromise = userPromise.then(user => fetchProfile(user.id))
515
+
516
+ const [user, config, profile] = await Promise.all([
517
+ userPromise,
518
+ fetchConfig(),
519
+ profilePromise
520
+ ])
521
+ ```
522
+
523
+ Reference: [https://github.com/shuding/better-all](https://github.com/shuding/better-all)
524
+
525
+ ---
526
+
527
+ ### Rule: async-parallel
528
+
529
+ ---
530
+ title: Promise.all() for Independent Operations
531
+ impact: CRITICAL
532
+ impactDescription: 2-10× improvement
533
+ tags: async, parallelization, promises, waterfalls
534
+ ---
535
+
536
+ ## Promise.all() for Independent Operations
537
+
538
+ When async operations have no interdependencies, execute them concurrently using `Promise.all()`.
539
+
540
+ **Incorrect (sequential execution, 3 round trips):**
541
+
542
+ ```typescript
543
+ const user = await fetchUser()
544
+ const posts = await fetchPosts()
545
+ const comments = await fetchComments()
546
+ ```
547
+
548
+ **Correct (parallel execution, 1 round trip):**
549
+
550
+ ```typescript
551
+ const [user, posts, comments] = await Promise.all([
552
+ fetchUser(),
553
+ fetchPosts(),
554
+ fetchComments()
555
+ ])
556
+ ```
557
+
558
+ ---
559
+
560
+ ### Rule: async-suspense-boundaries
561
+
562
+ ---
563
+ title: Strategic Suspense Boundaries
564
+ impact: HIGH
565
+ impactDescription: faster initial paint
566
+ tags: async, suspense, streaming, layout-shift
567
+ ---
568
+
569
+ ## Strategic Suspense Boundaries
570
+
571
+ Instead of awaiting data in async components before returning JSX, use Suspense boundaries to show the wrapper UI faster while data loads.
572
+
573
+ **Incorrect (wrapper blocked by data fetching):**
574
+
575
+ ```tsx
576
+ async function Page() {
577
+ const data = await fetchData() // Blocks entire page
578
+
579
+ return (
580
+ <div>
581
+ <div>Sidebar</div>
582
+ <div>Header</div>
583
+ <div>
584
+ <DataDisplay data={data} />
585
+ </div>
586
+ <div>Footer</div>
587
+ </div>
588
+ )
589
+ }
590
+ ```
591
+
592
+ The entire layout waits for data even though only the middle section needs it.
593
+
594
+ **Correct (wrapper shows immediately, data streams in):**
595
+
596
+ ```tsx
597
+ function Page() {
598
+ return (
599
+ <div>
600
+ <div>Sidebar</div>
601
+ <div>Header</div>
602
+ <div>
603
+ <Suspense fallback={<Skeleton />}>
604
+ <DataDisplay />
605
+ </Suspense>
606
+ </div>
607
+ <div>Footer</div>
608
+ </div>
609
+ )
610
+ }
611
+
612
+ async function DataDisplay() {
613
+ const data = await fetchData() // Only blocks this component
614
+ return <div>{data.content}</div>
615
+ }
616
+ ```
617
+
618
+ Sidebar, Header, and Footer render immediately. Only DataDisplay waits for data.
619
+
620
+ **Alternative (share promise across components):**
621
+
622
+ ```tsx
623
+ function Page() {
624
+ // Start fetch immediately, but don't await
625
+ const dataPromise = fetchData()
626
+
627
+ return (
628
+ <div>
629
+ <div>Sidebar</div>
630
+ <div>Header</div>
631
+ <Suspense fallback={<Skeleton />}>
632
+ <DataDisplay dataPromise={dataPromise} />
633
+ <DataSummary dataPromise={dataPromise} />
634
+ </Suspense>
635
+ <div>Footer</div>
636
+ </div>
637
+ )
638
+ }
639
+
640
+ function DataDisplay({ dataPromise }: { dataPromise: Promise<Data> }) {
641
+ const data = use(dataPromise) // Unwraps the promise
642
+ return <div>{data.content}</div>
643
+ }
644
+
645
+ function DataSummary({ dataPromise }: { dataPromise: Promise<Data> }) {
646
+ const data = use(dataPromise) // Reuses the same promise
647
+ return <div>{data.summary}</div>
648
+ }
649
+ ```
650
+
651
+ Both components share the same promise, so only one fetch occurs. Layout renders immediately while both components wait together.
652
+
653
+ **When NOT to use this pattern:**
654
+
655
+ - Critical data needed for layout decisions (affects positioning)
656
+ - SEO-critical content above the fold
657
+ - Small, fast queries where suspense overhead isn't worth it
658
+ - When you want to avoid layout shift (loading → content jump)
659
+
660
+ **Trade-off:** Faster initial paint vs potential layout shift. Choose based on your UX priorities.
661
+
662
+ ---
663
+
664
+ ### Rule: bundle-barrel-imports
665
+
666
+ ---
667
+ title: Avoid Barrel File Imports
668
+ impact: CRITICAL
669
+ impactDescription: 200-800ms import cost, slow builds
670
+ tags: bundle, imports, tree-shaking, barrel-files, performance
671
+ ---
672
+
673
+ ## Avoid Barrel File Imports
674
+
675
+ Import directly from source files instead of barrel files to avoid loading thousands of unused modules. **Barrel files** are entry points that re-export multiple modules (e.g., `index.js` that does `export * from './module'`).
676
+
677
+ Popular icon and component libraries can have **up to 10,000 re-exports** in their entry file. For many React packages, **it takes 200-800ms just to import them**, affecting both development speed and production cold starts.
678
+
679
+ **Why tree-shaking doesn't help:** When a library is marked as external (not bundled), the bundler can't optimize it. If you bundle it to enable tree-shaking, builds become substantially slower analyzing the entire module graph.
680
+
681
+ **Incorrect (imports entire library):**
682
+
683
+ ```tsx
684
+ import { Check, X, Menu } from 'lucide-react'
685
+ // Loads 1,583 modules, takes ~2.8s extra in dev
686
+ // Runtime cost: 200-800ms on every cold start
687
+
688
+ import { Button, TextField } from '@mui/material'
689
+ // Loads 2,225 modules, takes ~4.2s extra in dev
690
+ ```
691
+
692
+ **Correct (imports only what you need):**
693
+
694
+ ```tsx
695
+ import Check from 'lucide-react/dist/esm/icons/check'
696
+ import X from 'lucide-react/dist/esm/icons/x'
697
+ import Menu from 'lucide-react/dist/esm/icons/menu'
698
+ // Loads only 3 modules (~2KB vs ~1MB)
699
+
700
+ import Button from '@mui/material/Button'
701
+ import TextField from '@mui/material/TextField'
702
+ // Loads only what you use
703
+ ```
704
+
705
+ **Alternative (Next.js 13.5+):**
706
+
707
+ ```js
708
+ // next.config.js - use optimizePackageImports
709
+ module.exports = {
710
+ experimental: {
711
+ optimizePackageImports: ['lucide-react', '@mui/material']
712
+ }
713
+ }
714
+
715
+ // Then you can keep the ergonomic barrel imports:
716
+ import { Check, X, Menu } from 'lucide-react'
717
+ // Automatically transformed to direct imports at build time
718
+ ```
719
+
720
+ Direct imports provide 15-70% faster dev boot, 28% faster builds, 40% faster cold starts, and significantly faster HMR.
721
+
722
+ Libraries commonly affected: `lucide-react`, `@mui/material`, `@mui/icons-material`, `@tabler/icons-react`, `react-icons`, `@headlessui/react`, `@radix-ui/react-*`, `lodash`, `ramda`, `date-fns`, `rxjs`, `react-use`.
723
+
724
+ Reference: [How we optimized package imports in Next.js](https://vercel.com/blog/how-we-optimized-package-imports-in-next-js)
725
+
726
+ ---
727
+
728
+ ### Rule: bundle-conditional
729
+
730
+ ---
731
+ title: Conditional Module Loading
732
+ impact: HIGH
733
+ impactDescription: loads large data only when needed
734
+ tags: bundle, conditional-loading, lazy-loading
735
+ ---
736
+
737
+ ## Conditional Module Loading
738
+
739
+ Load large data or modules only when a feature is activated.
740
+
741
+ **Example (lazy-load animation frames):**
742
+
743
+ ```tsx
744
+ function AnimationPlayer({ enabled, setEnabled }: { enabled: boolean; setEnabled: React.Dispatch<React.SetStateAction<boolean>> }) {
745
+ const [frames, setFrames] = useState<Frame[] | null>(null)
746
+
747
+ useEffect(() => {
748
+ if (enabled && !frames && typeof window !== 'undefined') {
749
+ import('./animation-frames.js')
750
+ .then(mod => setFrames(mod.frames))
751
+ .catch(() => setEnabled(false))
752
+ }
753
+ }, [enabled, frames, setEnabled])
754
+
755
+ if (!frames) return <Skeleton />
756
+ return <Canvas frames={frames} />
757
+ }
758
+ ```
759
+
760
+ The `typeof window !== 'undefined'` check prevents bundling this module for SSR, optimizing server bundle size and build speed.
761
+
762
+ ---
763
+
764
+ ### Rule: bundle-defer-third-party
765
+
766
+ ---
767
+ title: Defer Non-Critical Third-Party Libraries
768
+ impact: MEDIUM
769
+ impactDescription: loads after hydration
770
+ tags: bundle, third-party, analytics, defer
771
+ ---
772
+
773
+ ## Defer Non-Critical Third-Party Libraries
774
+
775
+ Analytics, logging, and error tracking don't block user interaction. Load them after hydration.
776
+
777
+ **Incorrect (blocks initial bundle):**
778
+
779
+ ```tsx
780
+ import { Analytics } from '@vercel/analytics/react'
781
+
782
+ export default function RootLayout({ children }) {
783
+ return (
784
+ <html>
785
+ <body>
786
+ {children}
787
+ <Analytics />
788
+ </body>
789
+ </html>
790
+ )
791
+ }
792
+ ```
793
+
794
+ **Correct (loads after hydration):**
795
+
796
+ ```tsx
797
+ import dynamic from 'next/dynamic'
798
+
799
+ const Analytics = dynamic(
800
+ () => import('@vercel/analytics/react').then(m => m.Analytics),
801
+ { ssr: false }
802
+ )
803
+
804
+ export default function RootLayout({ children }) {
805
+ return (
806
+ <html>
807
+ <body>
808
+ {children}
809
+ <Analytics />
810
+ </body>
811
+ </html>
812
+ )
813
+ }
814
+ ```
815
+
816
+ ---
817
+
818
+ ### Rule: bundle-dynamic-imports
819
+
820
+ ---
821
+ title: Dynamic Imports for Heavy Components
822
+ impact: CRITICAL
823
+ impactDescription: directly affects TTI and LCP
824
+ tags: bundle, dynamic-import, code-splitting, next-dynamic
825
+ ---
826
+
827
+ ## Dynamic Imports for Heavy Components
828
+
829
+ Use `next/dynamic` to lazy-load large components not needed on initial render.
830
+
831
+ **Incorrect (Monaco bundles with main chunk ~300KB):**
832
+
833
+ ```tsx
834
+ import { MonacoEditor } from './monaco-editor'
835
+
836
+ function CodePanel({ code }: { code: string }) {
837
+ return <MonacoEditor value={code} />
838
+ }
839
+ ```
840
+
841
+ **Correct (Monaco loads on demand):**
842
+
843
+ ```tsx
844
+ import dynamic from 'next/dynamic'
845
+
846
+ const MonacoEditor = dynamic(
847
+ () => import('./monaco-editor').then(m => m.MonacoEditor),
848
+ { ssr: false }
849
+ )
850
+
851
+ function CodePanel({ code }: { code: string }) {
852
+ return <MonacoEditor value={code} />
853
+ }
854
+ ```
855
+
856
+ ---
857
+
858
+ ### Rule: bundle-preload
859
+
860
+ ---
861
+ title: Preload Based on User Intent
862
+ impact: MEDIUM
863
+ impactDescription: reduces perceived latency
864
+ tags: bundle, preload, user-intent, hover
865
+ ---
866
+
867
+ ## Preload Based on User Intent
868
+
869
+ Preload heavy bundles before they're needed to reduce perceived latency.
870
+
871
+ **Example (preload on hover/focus):**
872
+
873
+ ```tsx
874
+ function EditorButton({ onClick }: { onClick: () => void }) {
875
+ const preload = () => {
876
+ if (typeof window !== 'undefined') {
877
+ void import('./monaco-editor')
878
+ }
879
+ }
880
+
881
+ return (
882
+ <button
883
+ onMouseEnter={preload}
884
+ onFocus={preload}
885
+ onClick={onClick}
886
+ >
887
+ Open Editor
888
+ </button>
889
+ )
890
+ }
891
+ ```
892
+
893
+ **Example (preload when feature flag is enabled):**
894
+
895
+ ```tsx
896
+ function FlagsProvider({ children, flags }: Props) {
897
+ useEffect(() => {
898
+ if (flags.editorEnabled && typeof window !== 'undefined') {
899
+ void import('./monaco-editor').then(mod => mod.init())
900
+ }
901
+ }, [flags.editorEnabled])
902
+
903
+ return <FlagsContext.Provider value={flags}>
904
+ {children}
905
+ </FlagsContext.Provider>
906
+ }
907
+ ```
908
+
909
+ The `typeof window !== 'undefined'` check prevents bundling preloaded modules for SSR, optimizing server bundle size and build speed.
910
+
911
+ ---
912
+
913
+ ### Rule: client-event-listeners
914
+
915
+ ---
916
+ title: Deduplicate Global Event Listeners
917
+ impact: LOW
918
+ impactDescription: single listener for N components
919
+ tags: client, swr, event-listeners, subscription
920
+ ---
921
+
922
+ ## Deduplicate Global Event Listeners
923
+
924
+ Use `useSWRSubscription()` to share global event listeners across component instances.
925
+
926
+ **Incorrect (N instances = N listeners):**
927
+
928
+ ```tsx
929
+ function useKeyboardShortcut(key: string, callback: () => void) {
930
+ useEffect(() => {
931
+ const handler = (e: KeyboardEvent) => {
932
+ if (e.metaKey && e.key === key) {
933
+ callback()
934
+ }
935
+ }
936
+ window.addEventListener('keydown', handler)
937
+ return () => window.removeEventListener('keydown', handler)
938
+ }, [key, callback])
939
+ }
940
+ ```
941
+
942
+ When using the `useKeyboardShortcut` hook multiple times, each instance will register a new listener.
943
+
944
+ **Correct (N instances = 1 listener):**
945
+
946
+ ```tsx
947
+ import useSWRSubscription from 'swr/subscription'
948
+
949
+ // Module-level Map to track callbacks per key
950
+ const keyCallbacks = new Map<string, Set<() => void>>()
951
+
952
+ function useKeyboardShortcut(key: string, callback: () => void) {
953
+ // Register this callback in the Map
954
+ useEffect(() => {
955
+ if (!keyCallbacks.has(key)) {
956
+ keyCallbacks.set(key, new Set())
957
+ }
958
+ keyCallbacks.get(key)!.add(callback)
959
+
960
+ return () => {
961
+ const set = keyCallbacks.get(key)
962
+ if (set) {
963
+ set.delete(callback)
964
+ if (set.size === 0) {
965
+ keyCallbacks.delete(key)
966
+ }
967
+ }
968
+ }
969
+ }, [key, callback])
970
+
971
+ useSWRSubscription('global-keydown', () => {
972
+ const handler = (e: KeyboardEvent) => {
973
+ if (e.metaKey && keyCallbacks.has(e.key)) {
974
+ keyCallbacks.get(e.key)!.forEach(cb => cb())
975
+ }
976
+ }
977
+ window.addEventListener('keydown', handler)
978
+ return () => window.removeEventListener('keydown', handler)
979
+ })
980
+ }
981
+
982
+ function Profile() {
983
+ // Multiple shortcuts will share the same listener
984
+ useKeyboardShortcut('p', () => { /* ... */ })
985
+ useKeyboardShortcut('k', () => { /* ... */ })
986
+ // ...
987
+ }
988
+ ```
989
+
990
+ ---
991
+
992
+ ### Rule: client-localstorage-schema
993
+
994
+ ---
995
+ title: Version and Minimize localStorage Data
996
+ impact: MEDIUM
997
+ impactDescription: prevents schema conflicts, reduces storage size
998
+ tags: client, localStorage, storage, versioning, data-minimization
999
+ ---
1000
+
1001
+ ## Version and Minimize localStorage Data
1002
+
1003
+ Add version prefix to keys and store only needed fields. Prevents schema conflicts and accidental storage of sensitive data.
1004
+
1005
+ **Incorrect:**
1006
+
1007
+ ```typescript
1008
+ // No version, stores everything, no error handling
1009
+ localStorage.setItem('userConfig', JSON.stringify(fullUserObject))
1010
+ const data = localStorage.getItem('userConfig')
1011
+ ```
1012
+
1013
+ **Correct:**
1014
+
1015
+ ```typescript
1016
+ const VERSION = 'v2'
1017
+
1018
+ function saveConfig(config: { theme: string; language: string }) {
1019
+ try {
1020
+ localStorage.setItem(`userConfig:${VERSION}`, JSON.stringify(config))
1021
+ } catch {
1022
+ // Throws in incognito/private browsing, quota exceeded, or disabled
1023
+ }
1024
+ }
1025
+
1026
+ function loadConfig() {
1027
+ try {
1028
+ const data = localStorage.getItem(`userConfig:${VERSION}`)
1029
+ return data ? JSON.parse(data) : null
1030
+ } catch {
1031
+ return null
1032
+ }
1033
+ }
1034
+
1035
+ // Migration from v1 to v2
1036
+ function migrate() {
1037
+ try {
1038
+ const v1 = localStorage.getItem('userConfig:v1')
1039
+ if (v1) {
1040
+ const old = JSON.parse(v1)
1041
+ saveConfig({ theme: old.darkMode ? 'dark' : 'light', language: old.lang })
1042
+ localStorage.removeItem('userConfig:v1')
1043
+ }
1044
+ } catch {}
1045
+ }
1046
+ ```
1047
+
1048
+ **Store minimal fields from server responses:**
1049
+
1050
+ ```typescript
1051
+ // User object has 20+ fields, only store what UI needs
1052
+ function cachePrefs(user: FullUser) {
1053
+ try {
1054
+ localStorage.setItem('prefs:v1', JSON.stringify({
1055
+ theme: user.preferences.theme,
1056
+ notifications: user.preferences.notifications
1057
+ }))
1058
+ } catch {}
1059
+ }
1060
+ ```
1061
+
1062
+ **Always wrap in try-catch:** `getItem()` and `setItem()` throw in incognito/private browsing (Safari, Firefox), when quota exceeded, or when disabled.
1063
+
1064
+ **Benefits:** Schema evolution via versioning, reduced storage size, prevents storing tokens/PII/internal flags.
1065
+
1066
+ ---
1067
+
1068
+ ### Rule: client-passive-event-listeners
1069
+
1070
+ ---
1071
+ title: Use Passive Event Listeners for Scrolling Performance
1072
+ impact: MEDIUM
1073
+ impactDescription: eliminates scroll delay caused by event listeners
1074
+ tags: client, event-listeners, scrolling, performance, touch, wheel
1075
+ ---
1076
+
1077
+ ## Use Passive Event Listeners for Scrolling Performance
1078
+
1079
+ Add `{ passive: true }` to touch and wheel event listeners to enable immediate scrolling. Browsers normally wait for listeners to finish to check if `preventDefault()` is called, causing scroll delay.
1080
+
1081
+ **Incorrect:**
1082
+
1083
+ ```typescript
1084
+ useEffect(() => {
1085
+ const handleTouch = (e: TouchEvent) => console.log(e.touches[0].clientX)
1086
+ const handleWheel = (e: WheelEvent) => console.log(e.deltaY)
1087
+
1088
+ document.addEventListener('touchstart', handleTouch)
1089
+ document.addEventListener('wheel', handleWheel)
1090
+
1091
+ return () => {
1092
+ document.removeEventListener('touchstart', handleTouch)
1093
+ document.removeEventListener('wheel', handleWheel)
1094
+ }
1095
+ }, [])
1096
+ ```
1097
+
1098
+ **Correct:**
1099
+
1100
+ ```typescript
1101
+ useEffect(() => {
1102
+ const handleTouch = (e: TouchEvent) => console.log(e.touches[0].clientX)
1103
+ const handleWheel = (e: WheelEvent) => console.log(e.deltaY)
1104
+
1105
+ document.addEventListener('touchstart', handleTouch, { passive: true })
1106
+ document.addEventListener('wheel', handleWheel, { passive: true })
1107
+
1108
+ return () => {
1109
+ document.removeEventListener('touchstart', handleTouch)
1110
+ document.removeEventListener('wheel', handleWheel)
1111
+ }
1112
+ }, [])
1113
+ ```
1114
+
1115
+ **Use passive when:** tracking/analytics, logging, any listener that doesn't call `preventDefault()`.
1116
+
1117
+ **Don't use passive when:** implementing custom swipe gestures, custom zoom controls, or any listener that needs `preventDefault()`.
1118
+
1119
+ ---
1120
+
1121
+ ### Rule: client-swr-dedup
1122
+
1123
+ ---
1124
+ title: Use SWR for Automatic Deduplication
1125
+ impact: MEDIUM-HIGH
1126
+ impactDescription: automatic deduplication
1127
+ tags: client, swr, deduplication, data-fetching
1128
+ ---
1129
+
1130
+ ## Use SWR for Automatic Deduplication
1131
+
1132
+ SWR enables request deduplication, caching, and revalidation across component instances.
1133
+
1134
+ **Incorrect (no deduplication, each instance fetches):**
1135
+
1136
+ ```tsx
1137
+ function UserList() {
1138
+ const [users, setUsers] = useState([])
1139
+ useEffect(() => {
1140
+ fetch('/api/users')
1141
+ .then(r => r.json())
1142
+ .then(setUsers)
1143
+ }, [])
1144
+ }
1145
+ ```
1146
+
1147
+ **Correct (multiple instances share one request):**
1148
+
1149
+ ```tsx
1150
+ import useSWR from 'swr'
1151
+
1152
+ function UserList() {
1153
+ const { data: users } = useSWR('/api/users', fetcher)
1154
+ }
1155
+ ```
1156
+
1157
+ **For immutable data:**
1158
+
1159
+ ```tsx
1160
+ import { useImmutableSWR } from '@/lib/swr'
1161
+
1162
+ function StaticContent() {
1163
+ const { data } = useImmutableSWR('/api/config', fetcher)
1164
+ }
1165
+ ```
1166
+
1167
+ **For mutations:**
1168
+
1169
+ ```tsx
1170
+ import { useSWRMutation } from 'swr/mutation'
1171
+
1172
+ function UpdateButton() {
1173
+ const { trigger } = useSWRMutation('/api/user', updateUser)
1174
+ return <button onClick={() => trigger()}>Update</button>
1175
+ }
1176
+ ```
1177
+
1178
+ Reference: [https://swr.vercel.app](https://swr.vercel.app)
1179
+
1180
+ ---
1181
+
1182
+ ### Rule: engineering-spec
1183
+
1184
+ ---
1185
+ title: Next.js Pro — Engineering Specification
1186
+ impact: MEDIUM
1187
+ tags: nextjs-pro
1188
+ ---
1189
+
1190
+ # Next.js Pro — Engineering Specification
1191
+
1192
+ > Production-grade specification for Next.js App Router and React performance patterns at FAANG scale.
1193
+
1194
+ ---
1195
+
1196
+ ## 1. Overview
1197
+
1198
+ Next.js Pro provides structured decision frameworks for Next.js App Router development: Server vs Client component routing, data fetching strategy (static/ISR/dynamic), routing conventions, caching patterns, and 60+ performance rules. The skill operates as an **Expert (decision tree)** — it produces component type decisions, data fetching strategy selections, routing guidance, and performance recommendations. It does not create projects, write components, or execute builds.
1199
+
1200
+ **Contract Version:** 2.0.0
1201
+ **Backward Compatibility:** breaking (first hardened version)
1202
+ **Breaking Changes:** None — new spec for first hardening
1203
+
1204
+ ---
1205
+
1206
+ ## 2. Problem Statement
1207
+
1208
+ Next.js development at scale faces four quantified problems:
1209
+
1210
+ | Problem | Measurement | Impact |
1211
+ |---------|-------------|--------|
1212
+ | Unnecessary `'use client'` | 60% of components marked client when not needed | Larger bundles, no SSR |
1213
+ | Data fetching waterfalls | 45% of pages have sequential fetches | Slow TTFB |
1214
+ | Barrel import bloat | 35% of imports use barrel files (`index.js`) | Tree-shaking failure |
1215
+ | Missing loading/error states | 50% of routes lack loading.tsx or error.tsx | Poor UX |
1216
+
1217
+ Next.js Pro eliminates these with Server-first defaults (no directive = Server Component), parallel data fetching, direct imports, and mandatory route file conventions.
1218
+
1219
+ ---
1220
+
1221
+ ## 3. Design Goals
1222
+
1223
+ | ID | Goal | Measurable Constraint |
1224
+ |----|------|-----------------------|
1225
+ | G1 | Server/Client decision tree | Binary: needs useState/useEffect/events → client; else → server |
1226
+ | G2 | Data fetching routing | 3 strategies: static (default), ISR (revalidate: 60), dynamic (no-store) |
1227
+ | G3 | Route conventions | 4 files: page.tsx, layout.tsx, loading.tsx, error.tsx |
1228
+ | G4 | 60+ performance rules | 7 categories: async, bundle, server, client, rendering, rerender, js |
1229
+ | G5 | Core Web Vitals pass | LCP < 2.5s, INP < 200ms, CLS < 0.1 |
1230
+ | G6 | Server-first philosophy | Server Component by default; client only when necessary |
1231
+
1232
+ ---
1233
+
1234
+ ## 4. Non-Goals
1235
+
1236
+ | ID | Excluded | Rationale |
1237
+ |----|----------|-----------|
1238
+ | NG1 | React component architecture | Owned by `react-architect` skill |
1239
+ | NG2 | CSS/Tailwind styling | Owned by `tailwind-kit` skill |
1240
+ | NG3 | Design system | Owned by `design-system` skill |
1241
+ | NG4 | Performance profiling | Owned by `perf-optimizer` skill |
1242
+ | NG5 | API route design | Owned by `api-architect` skill |
1243
+ | NG6 | Deployment pipeline | Owned by `cicd-pipeline` skill |
1244
+
1245
+ ---
1246
+
1247
+ ## 5. System Boundaries
1248
+
1249
+ | Boundary | Owned | Not Owned |
1250
+ |----------|-------|-----------|
1251
+ | Server/Client component decision | Decision tree | Component implementation |
1252
+ | Data fetching strategy (3 options) | Strategy selection | API design |
1253
+ | Routing conventions (4 files) | Convention guidance | Route implementation |
1254
+ | 60+ performance rules | Pattern guidance | Performance measurement |
1255
+ | Core Web Vitals targets | Target definition | Vitals measurement |
1256
+ | Caching strategy (static/ISR/dynamic) | Strategy selection | Cache infrastructure |
1257
+
1258
+ **Side-effect boundary:** Next.js Pro produces decisions, patterns, and rule references. It does not create files, build projects, or measure performance.
1259
+
1260
+ ---
1261
+
1262
+ ## 6. Integration Model
1263
+
1264
+ ### 6.1 Agent Contract
1265
+
1266
+ #### Input Schema
1267
+
1268
+ ```
1269
+ Request_Type: string # "component-type" | "data-fetching" | "routing" |
1270
+ # "performance-rules" | "project-structure" | "full-guide"
1271
+ Context: {
1272
+ needs_interactivity: boolean # useState, useEffect, event handlers
1273
+ data_volatility: string # "static" | "periodic" | "real-time"
1274
+ revalidate_seconds: number | null # ISR interval (default: 60)
1275
+ route_group: string | null # Route group name (e.g., "(marketing)")
1276
+ rule_category: string | null # "async" | "bundle" | "server" | "client" |
1277
+ # "rendering" | "rerender" | "js"
1278
+ }
1279
+ contract_version: string # "2.0.0"
1280
+ ```
1281
+
1282
+ #### Output Schema
1283
+
1284
+ ```
1285
+ Status: "success" | "error"
1286
+ Data: {
1287
+ component_type: {
1288
+ type: string # "server" | "client"
1289
+ directive: string | null # "'use client'" or null (server default)
1290
+ rationale: string
1291
+ } | null
1292
+ data_fetching: {
1293
+ strategy: string # "static" | "isr" | "dynamic"
1294
+ cache_config: string # "default" | "revalidate: N" | "no-store"
1295
+ use_case: string
1296
+ } | null
1297
+ routing: {
1298
+ files: Array<{
1299
+ name: string # "page.tsx" | "layout.tsx" | "loading.tsx" | "error.tsx"
1300
+ purpose: string
1301
+ required: boolean
1302
+ }>
1303
+ } | null
1304
+ rules: {
1305
+ category: string
1306
+ file_count: number
1307
+ rule_files: Array<string> # File paths in rules/
1308
+ } | null
1309
+ project_structure: {
1310
+ template: string # Directory structure recommendation
1311
+ } | null
1312
+ metadata: {
1313
+ contract_version: string
1314
+ backward_compatibility: string
1315
+ }
1316
+ }
1317
+ Error: ErrorSchema | null
1318
+ ```
1319
+
1320
+ #### Error Schema
1321
+
1322
+ ```
1323
+ Code: string # From Error Taxonomy (Section 11)
1324
+ Message: string
1325
+ Request_Type: string
1326
+ Recoverable: boolean
1327
+ ```
1328
+
1329
+ #### Deterministic Guarantees
1330
+
1331
+ - Component type is binary: needs_interactivity=true → `'use client'`; false → Server Component (no directive).
1332
+ - Data fetching is deterministic: static → default cache, periodic → `revalidate: N`, real-time → `no-store`.
1333
+ - Route conventions are fixed: page.tsx, layout.tsx, loading.tsx, error.tsx.
1334
+ - Rule categories are fixed: 7 categories with fixed file counts.
1335
+ - Core Web Vitals targets are fixed: LCP < 2.5s, INP < 200ms, CLS < 0.1.
1336
+ - Default ISR interval is 60 seconds.
1337
+
1338
+ #### What Agents May Assume
1339
+
1340
+ - Server Component is the default (no directive needed).
1341
+ - Data fetching in Server Components uses native `fetch()`.
1342
+ - Route file conventions follow App Router specification.
1343
+ - Performance rules cover the 7 documented categories.
1344
+
1345
+ #### What Agents Must NOT Assume
1346
+
1347
+ - All components need `'use client'`.
1348
+ - Client-side data fetching is the default pattern.
1349
+ - Barrel imports are acceptable.
1350
+ - Loading/error states are optional.
1351
+
1352
+ #### Side-Effect Boundaries
1353
+
1354
+ | Operation | Side Effects |
1355
+ |-----------|-------------|
1356
+ | Component type | None; type recommendation |
1357
+ | Data fetching | None; strategy recommendation |
1358
+ | Routing | None; file convention guidance |
1359
+ | Performance rules | None; rule references |
1360
+ | Project structure | None; template output |
1361
+ | Full guide | None; combined output |
1362
+
1363
+ ### 6.2 Workflow Contract
1364
+
1365
+ #### Invocation Pattern
1366
+
1367
+ ```
1368
+ 1. Identify component requirements (interactivity, data needs)
1369
+ 2. Invoke component-type for Server vs Client decision
1370
+ 3. Invoke data-fetching for cache strategy
1371
+ 4. Invoke routing for file conventions
1372
+ 5. Invoke performance-rules for category-specific guidance
1373
+ 6. Read specific rule files from rules/ (caller's responsibility)
1374
+ 7. Implement patterns (caller's responsibility)
1375
+ ```
1376
+
1377
+ #### Execution Guarantees
1378
+
1379
+ - Each invocation produces a complete recommendation.
1380
+ - All decisions are independent (can be invoked in any order).
1381
+
1382
+ #### Failure Propagation Model
1383
+
1384
+ | Failure Severity | Propagation | Workflow Action |
1385
+ |-----------------|-------------|-----------------|
1386
+ | Invalid request type | Return error | Use supported type |
1387
+ | Unknown rule category | Return error | Use one of 7 categories |
1388
+ | Missing interactivity flag | Default to false (server) | Transparent |
1389
+
1390
+ #### Retry Boundaries
1391
+
1392
+ - Zero internal retries. Deterministic output.
1393
+
1394
+ #### Isolation Model
1395
+
1396
+ - Each invocation is stateless and independent.
1397
+
1398
+ #### Idempotency Expectations
1399
+
1400
+ | Operation | Idempotent | Notes |
1401
+ |-----------|-----------|-------|
1402
+ | Component type | Yes | Same interactivity = same type |
1403
+ | Data fetching | Yes | Same volatility = same strategy |
1404
+ | Routing | Yes | Fixed conventions |
1405
+ | Performance rules | Yes | Fixed categories + files |
1406
+ | Project structure | Yes | Fixed template |
1407
+
1408
+ ---
1409
+
1410
+ ## 7. Execution Model
1411
+
1412
+ ### 2-Phase Lifecycle
1413
+
1414
+ | Phase | Action | Output |
1415
+ |-------|--------|--------|
1416
+ | **Classify** | Validate context, determine request type | Classification |
1417
+ | **Recommend** | Generate type, strategy, or rule references | Complete output |
1418
+
1419
+ All phases synchronous. No async pipeline.
1420
+
1421
+ ---
1422
+
1423
+ ## 8. Deterministic Design Principles
1424
+
1425
+ | Principle | Enforcement |
1426
+ |-----------|-------------|
1427
+ | Server-first | No directive = Server Component; explicit `'use client'` required |
1428
+ | Binary component decision | needs_interactivity → client; else → server |
1429
+ | Fixed data strategies | static (default), ISR (revalidate: 60), dynamic (no-store) |
1430
+ | Fixed route files | page.tsx, layout.tsx, loading.tsx, error.tsx |
1431
+ | Fixed CWV targets | LCP < 2.5s, INP < 200ms, CLS < 0.1 |
1432
+ | No barrel imports | Direct imports only; barrel files break tree-shaking |
1433
+ | Parallel fetching | No nested awaits; use Promise.all() |
1434
+ | Suspense boundaries | Use loading.tsx for route-level, `<Suspense>` for component-level |
1435
+ | 7 rule categories | async (5), bundle (5), server (8), client (4), rendering (10), rerender (12), js (11) |
1436
+
1437
+ ---
1438
+
1439
+ ## 9. State & Idempotency Model
1440
+
1441
+ Stateless. Fully idempotent. No persistent state.
1442
+
1443
+ ---
1444
+
1445
+ ## 10. Failure Handling Strategy
1446
+
1447
+ | Failure Class | Behavior | Caller Recovery |
1448
+ |---------------|----------|-----------------|
1449
+ | Unknown rule category | Return `ERR_UNKNOWN_CATEGORY` | Use one of 7 categories |
1450
+ | Invalid request type | Return `ERR_INVALID_REQUEST_TYPE` | Use supported type |
1451
+ | Invalid data volatility | Return `ERR_INVALID_VOLATILITY` | Use static, periodic, or real-time |
1452
+
1453
+ **Invariant:** Every failure returns a structured error. No partial recommendations.
1454
+
1455
+ ---
1456
+
1457
+ ## 11. Error Taxonomy
1458
+
1459
+ | Code | Category | Recoverable | Description |
1460
+ |------|----------|-------------|-------------|
1461
+ | `ERR_INVALID_REQUEST_TYPE` | Validation | No | Request type not supported |
1462
+ | `ERR_UNKNOWN_CATEGORY` | Validation | Yes | Rule category not one of 7 |
1463
+ | `ERR_INVALID_VOLATILITY` | Validation | Yes | Data volatility not recognized |
1464
+
1465
+ ---
1466
+
1467
+ ## 12. Timeout & Retry Policy
1468
+
1469
+ | Parameter | Default | Maximum | Rationale |
1470
+ |-----------|---------|---------|-----------|
1471
+ | Decision generation | N/A | N/A | Synchronous; < 50ms |
1472
+ | Internal retries | Zero | Zero | Deterministic output |
1473
+
1474
+ ---
1475
+
1476
+ ## 13. Observability & Logging Schema
1477
+
1478
+ ### Log Entry Format
1479
+
1480
+ ```json
1481
+ {
1482
+ "trace_id": "uuid",
1483
+ "skill_name": "nextjs-pro",
1484
+ "contract_version": "2.0.0",
1485
+ "execution_id": "uuid",
1486
+ "timestamp": "ISO-8601",
1487
+ "request_type": "string",
1488
+ "component_type": "server|client|null",
1489
+ "data_strategy": "static|isr|dynamic|null",
1490
+ "rule_category": "string|null",
1491
+ "status": "success|error",
1492
+ "error_code": "string|null",
1493
+ "duration_ms": "number"
1494
+ }
1495
+ ```
1496
+
1497
+ ### Required Log Points
1498
+
1499
+ | Event | Log Level | Fields |
1500
+ |-------|-----------|--------|
1501
+ | Component type decided | INFO | component_type, directive |
1502
+ | Data strategy selected | INFO | data_strategy, revalidate_seconds |
1503
+ | Rules referenced | INFO | rule_category, file_count |
1504
+ | Decision failed | ERROR | error_code, message |
1505
+
1506
+ ### Metrics
1507
+
1508
+ | Metric | Type | Unit |
1509
+ |--------|------|------|
1510
+ | `nextjspro.decision.duration` | Histogram | ms |
1511
+ | `nextjspro.component_type.distribution` | Counter | server vs client |
1512
+ | `nextjspro.data_strategy.distribution` | Counter | static vs isr vs dynamic |
1513
+ | `nextjspro.rule_category.distribution` | Counter | per category |
1514
+
1515
+ ---
1516
+
1517
+ ## 14. Security & Trust Model
1518
+
1519
+ ### Data Handling
1520
+
1521
+ - Next.js Pro processes no credentials, API keys, or PII.
1522
+ - All guidance is framework-specific best practices.
1523
+ - No network calls, no file access.
1524
+
1525
+ ### Server Action Security
1526
+
1527
+ | Rule | Enforcement |
1528
+ |------|-------------|
1529
+ | Always validate inputs in Server Actions | Never trust client data |
1530
+ | Use `'use server'` directive explicitly | Marks server-only code |
1531
+ | Authenticate Server Actions | Check session before mutation |
1532
+
1533
+ ---
1534
+
1535
+ ## 15. Scalability Model
1536
+
1537
+ | Dimension | Constraint | Mitigation |
1538
+ |-----------|-----------|------------|
1539
+ | Throughput | CPU-bound decision tree | < 50ms; scales linearly |
1540
+ | Concurrency | Stateless invocations | Unlimited parallel |
1541
+ | Rule storage | 60 files (~100 KB total) | Static; growth bounded |
1542
+ | Memory per invocation | < 1 MB | No accumulation |
1543
+ | Network | Zero network calls | No external dependency |
1544
+
1545
+ ---
1546
+
1547
+ ## 16. Concurrency Model
1548
+
1549
+ Fully parallel. No shared state. No coordination required.
1550
+
1551
+ ---
1552
+
1553
+ ## 17. Resource Lifecycle Management
1554
+
1555
+ All resources scoped to invocation. No persistent handles.
1556
+
1557
+ ---
1558
+
1559
+ ## 18. Performance Constraints
1560
+
1561
+ | Operation | P50 Target | P99 Target | Hard Limit |
1562
+ |-----------|-----------|-----------|------------|
1563
+ | Component type decision | < 1 ms | < 3 ms | 10 ms |
1564
+ | Data strategy decision | < 1 ms | < 3 ms | 10 ms |
1565
+ | Rule category listing | < 2 ms | < 5 ms | 20 ms |
1566
+ | Full guide | < 10 ms | < 30 ms | 50 ms |
1567
+ | Output size | ≤ 1,500 chars | ≤ 4,000 chars | 6,000 chars |
1568
+
1569
+ ---
1570
+
1571
+ ## 19. Operational Risks
1572
+
1573
+ | Risk | Likelihood | Impact | Mitigation |
1574
+ |------|-----------|--------|------------|
1575
+ | Next.js API changes | Medium | Outdated patterns | Track Next.js releases |
1576
+ | React Server Components evolution | Medium | Convention changes | Review with major React releases |
1577
+ | Rule file count growth | Low | Context overload | Selective loading; read only needed rules |
1578
+ | CWV targets updated | Low | Targets misaligned | Track Google updates annually |
1579
+ | App Router vs Pages Router confusion | High | Wrong patterns applied | Always specify App Router only |
1580
+
1581
+ ---
1582
+
1583
+ ## 20. Compliance with skill-design-guide.md
1584
+
1585
+ | Requirement | Status | Evidence |
1586
+ |-------------|--------|----------|
1587
+ | YAML frontmatter complete | ✅ | name, description, metadata with category, version, triggers, coordinates_with, success_metrics |
1588
+ | SKILL.md < 200 lines | ✅ | Entry point under 200 lines |
1589
+ | Prerequisites documented | ✅ | No external dependencies (knowledge skill) |
1590
+ | When to Use section | ✅ | Situation-based routing table |
1591
+ | Core content matches skill type | ✅ | Expert type: decision trees, pattern guidance |
1592
+ | Troubleshooting section | ✅ | Anti-patterns table |
1593
+ | Related section | ✅ | Cross-links to react-architect, tailwind-kit, perf-optimizer |
1594
+ | Content Map for multi-file | ✅ | Links to 7 rule categories + engineering-spec.md |
1595
+ | Contract versioning | ✅ | contract_version, backward_compatibility, breaking_changes |
1596
+ | Compliance matrix structured | ✅ | This table with ✅/❌ + evidence |
1597
+
1598
+ ---
1599
+
1600
+ ## 21. Production Readiness Checklist
1601
+
1602
+ | Category | Check | Status |
1603
+ |----------|-------|--------|
1604
+ | **Functionality** | Server/Client binary decision tree | ✅ |
1605
+ | **Functionality** | Data fetching (3 strategies: static, ISR, dynamic) | ✅ |
1606
+ | **Functionality** | Route conventions (4 files) | ✅ |
1607
+ | **Functionality** | 60+ rules across 7 categories | ✅ |
1608
+ | **Functionality** | Core Web Vitals targets (LCP, INP, CLS) | ✅ |
1609
+ | **Contracts** | Input/output/error schemas in pseudo-schema format | ✅ |
1610
+ | **Contracts** | Contract versioning with semver | ✅ |
1611
+ | **Failure** | Error taxonomy with 3 categorized codes | ✅ |
1612
+ | **Failure** | Zero internal retries | ✅ |
1613
+ | **Determinism** | Fixed component decision, fixed strategies, fixed rules | ✅ |
1614
+ | **Security** | No credentials, no PII, no file access | ✅ |
1615
+ | **Observability** | Structured log schema with 5 mandatory fields | ✅ |
1616
+ | **Observability** | 4 metrics defined | ✅ |
1617
+ | **Performance** | P50/P99 targets for all operations | ✅ |
1618
+ | **Scalability** | Stateless; unlimited parallel | ✅ |
1619
+ | **Compliance** | All skill-design-guide.md sections mapped with evidence | ✅ |
1620
+
1621
+ ---
1622
+
1623
+
1624
+
1625
+ ---
1626
+
1627
+ ### Rule: js-batch-dom-css
1628
+
1629
+ ---
1630
+ title: Avoid Layout Thrashing
1631
+ impact: MEDIUM
1632
+ impactDescription: prevents forced synchronous layouts and reduces performance bottlenecks
1633
+ tags: javascript, dom, css, performance, reflow, layout-thrashing
1634
+ ---
1635
+
1636
+ ## Avoid Layout Thrashing
1637
+
1638
+ Avoid interleaving style writes with layout reads. When you read a layout property (like `offsetWidth`, `getBoundingClientRect()`, or `getComputedStyle()`) between style changes, the browser is forced to trigger a synchronous reflow.
1639
+
1640
+ **This is OK (browser batches style changes):**
1641
+ ```typescript
1642
+ function updateElementStyles(element: HTMLElement) {
1643
+ // Each line invalidates style, but browser batches the recalculation
1644
+ element.style.width = '100px'
1645
+ element.style.height = '200px'
1646
+ element.style.backgroundColor = 'blue'
1647
+ element.style.border = '1px solid black'
1648
+ }
1649
+ ```
1650
+
1651
+ **Incorrect (interleaved reads and writes force reflows):**
1652
+ ```typescript
1653
+ function layoutThrashing(element: HTMLElement) {
1654
+ element.style.width = '100px'
1655
+ const width = element.offsetWidth // Forces reflow
1656
+ element.style.height = '200px'
1657
+ const height = element.offsetHeight // Forces another reflow
1658
+ }
1659
+ ```
1660
+
1661
+ **Correct (batch writes, then read once):**
1662
+ ```typescript
1663
+ function updateElementStyles(element: HTMLElement) {
1664
+ // Batch all writes together
1665
+ element.style.width = '100px'
1666
+ element.style.height = '200px'
1667
+ element.style.backgroundColor = 'blue'
1668
+ element.style.border = '1px solid black'
1669
+
1670
+ // Read after all writes are done (single reflow)
1671
+ const { width, height } = element.getBoundingClientRect()
1672
+ }
1673
+ ```
1674
+
1675
+ **Correct (batch reads, then writes):**
1676
+ ```typescript
1677
+ function avoidThrashing(element: HTMLElement) {
1678
+ // Read phase - all layout queries first
1679
+ const rect1 = element.getBoundingClientRect()
1680
+ const offsetWidth = element.offsetWidth
1681
+ const offsetHeight = element.offsetHeight
1682
+
1683
+ // Write phase - all style changes after
1684
+ element.style.width = '100px'
1685
+ element.style.height = '200px'
1686
+ }
1687
+ ```
1688
+
1689
+ **Better: use CSS classes**
1690
+ ```css
1691
+ .highlighted-box {
1692
+ width: 100px;
1693
+ height: 200px;
1694
+ background-color: blue;
1695
+ border: 1px solid black;
1696
+ }
1697
+ ```
1698
+ ```typescript
1699
+ function updateElementStyles(element: HTMLElement) {
1700
+ element.classList.add('highlighted-box')
1701
+
1702
+ const { width, height } = element.getBoundingClientRect()
1703
+ }
1704
+ ```
1705
+
1706
+ **React example:**
1707
+ ```tsx
1708
+ // Incorrect: interleaving style changes with layout queries
1709
+ function Box({ isHighlighted }: { isHighlighted: boolean }) {
1710
+ const ref = useRef<HTMLDivElement>(null)
1711
+
1712
+ useEffect(() => {
1713
+ if (ref.current && isHighlighted) {
1714
+ ref.current.style.width = '100px'
1715
+ const width = ref.current.offsetWidth // Forces layout
1716
+ ref.current.style.height = '200px'
1717
+ }
1718
+ }, [isHighlighted])
1719
+
1720
+ return <div ref={ref}>Content</div>
1721
+ }
1722
+
1723
+ // Correct: toggle class
1724
+ function Box({ isHighlighted }: { isHighlighted: boolean }) {
1725
+ return (
1726
+ <div className={isHighlighted ? 'highlighted-box' : ''}>
1727
+ Content
1728
+ </div>
1729
+ )
1730
+ }
1731
+ ```
1732
+
1733
+ Prefer CSS classes over inline styles when possible. CSS files are cached by the browser, and classes provide better separation of concerns and are easier to maintain.
1734
+
1735
+ See [this gist](https://gist.github.com/paulirish/5d52fb081b3570c81e3a) and [CSS Triggers](https://csstriggers.com/) for more information on layout-forcing operations.
1736
+
1737
+ ---
1738
+
1739
+ ### Rule: js-cache-function-results
1740
+
1741
+ ---
1742
+ title: Cache Repeated Function Calls
1743
+ impact: MEDIUM
1744
+ impactDescription: avoid redundant computation
1745
+ tags: javascript, cache, memoization, performance
1746
+ ---
1747
+
1748
+ ## Cache Repeated Function Calls
1749
+
1750
+ Use a module-level Map to cache function results when the same function is called repeatedly with the same inputs during render.
1751
+
1752
+ **Incorrect (redundant computation):**
1753
+
1754
+ ```typescript
1755
+ function ProjectList({ projects }: { projects: Project[] }) {
1756
+ return (
1757
+ <div>
1758
+ {projects.map(project => {
1759
+ // slugify() called 100+ times for same project names
1760
+ const slug = slugify(project.name)
1761
+
1762
+ return <ProjectCard key={project.id} slug={slug} />
1763
+ })}
1764
+ </div>
1765
+ )
1766
+ }
1767
+ ```
1768
+
1769
+ **Correct (cached results):**
1770
+
1771
+ ```typescript
1772
+ // Module-level cache
1773
+ const slugifyCache = new Map<string, string>()
1774
+
1775
+ function cachedSlugify(text: string): string {
1776
+ if (slugifyCache.has(text)) {
1777
+ return slugifyCache.get(text)!
1778
+ }
1779
+ const result = slugify(text)
1780
+ slugifyCache.set(text, result)
1781
+ return result
1782
+ }
1783
+
1784
+ function ProjectList({ projects }: { projects: Project[] }) {
1785
+ return (
1786
+ <div>
1787
+ {projects.map(project => {
1788
+ // Computed only once per unique project name
1789
+ const slug = cachedSlugify(project.name)
1790
+
1791
+ return <ProjectCard key={project.id} slug={slug} />
1792
+ })}
1793
+ </div>
1794
+ )
1795
+ }
1796
+ ```
1797
+
1798
+ **Simpler pattern for single-value functions:**
1799
+
1800
+ ```typescript
1801
+ let isLoggedInCache: boolean | null = null
1802
+
1803
+ function isLoggedIn(): boolean {
1804
+ if (isLoggedInCache !== null) {
1805
+ return isLoggedInCache
1806
+ }
1807
+
1808
+ isLoggedInCache = document.cookie.includes('auth=')
1809
+ return isLoggedInCache
1810
+ }
1811
+
1812
+ // Clear cache when auth changes
1813
+ function onAuthChange() {
1814
+ isLoggedInCache = null
1815
+ }
1816
+ ```
1817
+
1818
+ Use a Map (not a hook) so it works everywhere: utilities, event handlers, not just React components.
1819
+
1820
+ Reference: [How we made the Vercel Dashboard twice as fast](https://vercel.com/blog/how-we-made-the-vercel-dashboard-twice-as-fast)
1821
+
1822
+ ---
1823
+
1824
+ ### Rule: js-cache-property-access
1825
+
1826
+ ---
1827
+ title: Cache Property Access in Loops
1828
+ impact: LOW-MEDIUM
1829
+ impactDescription: reduces lookups
1830
+ tags: javascript, loops, optimization, caching
1831
+ ---
1832
+
1833
+ ## Cache Property Access in Loops
1834
+
1835
+ Cache object property lookups in hot paths.
1836
+
1837
+ **Incorrect (3 lookups × N iterations):**
1838
+
1839
+ ```typescript
1840
+ for (let i = 0; i < arr.length; i++) {
1841
+ process(obj.config.settings.value)
1842
+ }
1843
+ ```
1844
+
1845
+ **Correct (1 lookup total):**
1846
+
1847
+ ```typescript
1848
+ const value = obj.config.settings.value
1849
+ const len = arr.length
1850
+ for (let i = 0; i < len; i++) {
1851
+ process(value)
1852
+ }
1853
+ ```
1854
+
1855
+ ---
1856
+
1857
+ ### Rule: js-cache-storage
1858
+
1859
+ ---
1860
+ title: Cache Storage API Calls
1861
+ impact: LOW-MEDIUM
1862
+ impactDescription: reduces expensive I/O
1863
+ tags: javascript, localStorage, storage, caching, performance
1864
+ ---
1865
+
1866
+ ## Cache Storage API Calls
1867
+
1868
+ `localStorage`, `sessionStorage`, and `document.cookie` are synchronous and expensive. Cache reads in memory.
1869
+
1870
+ **Incorrect (reads storage on every call):**
1871
+
1872
+ ```typescript
1873
+ function getTheme() {
1874
+ return localStorage.getItem('theme') ?? 'light'
1875
+ }
1876
+ // Called 10 times = 10 storage reads
1877
+ ```
1878
+
1879
+ **Correct (Map cache):**
1880
+
1881
+ ```typescript
1882
+ const storageCache = new Map<string, string | null>()
1883
+
1884
+ function getLocalStorage(key: string) {
1885
+ if (!storageCache.has(key)) {
1886
+ storageCache.set(key, localStorage.getItem(key))
1887
+ }
1888
+ return storageCache.get(key)
1889
+ }
1890
+
1891
+ function setLocalStorage(key: string, value: string) {
1892
+ localStorage.setItem(key, value)
1893
+ storageCache.set(key, value) // keep cache in sync
1894
+ }
1895
+ ```
1896
+
1897
+ Use a Map (not a hook) so it works everywhere: utilities, event handlers, not just React components.
1898
+
1899
+ **Cookie caching:**
1900
+
1901
+ ```typescript
1902
+ let cookieCache: Record<string, string> | null = null
1903
+
1904
+ function getCookie(name: string) {
1905
+ if (!cookieCache) {
1906
+ cookieCache = Object.fromEntries(
1907
+ document.cookie.split('; ').map(c => c.split('='))
1908
+ )
1909
+ }
1910
+ return cookieCache[name]
1911
+ }
1912
+ ```
1913
+
1914
+ **Important (invalidate on external changes):**
1915
+
1916
+ If storage can change externally (another tab, server-set cookies), invalidate cache:
1917
+
1918
+ ```typescript
1919
+ window.addEventListener('storage', (e) => {
1920
+ if (e.key) storageCache.delete(e.key)
1921
+ })
1922
+
1923
+ document.addEventListener('visibilitychange', () => {
1924
+ if (document.visibilityState === 'visible') {
1925
+ storageCache.clear()
1926
+ }
1927
+ })
1928
+ ```
1929
+
1930
+ ---
1931
+
1932
+ ### Rule: js-combine-iterations
1933
+
1934
+ ---
1935
+ title: Combine Multiple Array Iterations
1936
+ impact: LOW-MEDIUM
1937
+ impactDescription: reduces iterations
1938
+ tags: javascript, arrays, loops, performance
1939
+ ---
1940
+
1941
+ ## Combine Multiple Array Iterations
1942
+
1943
+ Multiple `.filter()` or `.map()` calls iterate the array multiple times. Combine into one loop.
1944
+
1945
+ **Incorrect (3 iterations):**
1946
+
1947
+ ```typescript
1948
+ const admins = users.filter(u => u.isAdmin)
1949
+ const testers = users.filter(u => u.isTester)
1950
+ const inactive = users.filter(u => !u.isActive)
1951
+ ```
1952
+
1953
+ **Correct (1 iteration):**
1954
+
1955
+ ```typescript
1956
+ const admins: User[] = []
1957
+ const testers: User[] = []
1958
+ const inactive: User[] = []
1959
+
1960
+ for (const user of users) {
1961
+ if (user.isAdmin) admins.push(user)
1962
+ if (user.isTester) testers.push(user)
1963
+ if (!user.isActive) inactive.push(user)
1964
+ }
1965
+ ```
1966
+
1967
+ ---
1968
+
1969
+ ### Rule: js-early-exit
1970
+
1971
+ ---
1972
+ title: Early Return from Functions
1973
+ impact: LOW-MEDIUM
1974
+ impactDescription: avoids unnecessary computation
1975
+ tags: javascript, functions, optimization, early-return
1976
+ ---
1977
+
1978
+ ## Early Return from Functions
1979
+
1980
+ Return early when result is determined to skip unnecessary processing.
1981
+
1982
+ **Incorrect (processes all items even after finding answer):**
1983
+
1984
+ ```typescript
1985
+ function validateUsers(users: User[]) {
1986
+ let hasError = false
1987
+ let errorMessage = ''
1988
+
1989
+ for (const user of users) {
1990
+ if (!user.email) {
1991
+ hasError = true
1992
+ errorMessage = 'Email required'
1993
+ }
1994
+ if (!user.name) {
1995
+ hasError = true
1996
+ errorMessage = 'Name required'
1997
+ }
1998
+ // Continues checking all users even after error found
1999
+ }
2000
+
2001
+ return hasError ? { valid: false, error: errorMessage } : { valid: true }
2002
+ }
2003
+ ```
2004
+
2005
+ **Correct (returns immediately on first error):**
2006
+
2007
+ ```typescript
2008
+ function validateUsers(users: User[]) {
2009
+ for (const user of users) {
2010
+ if (!user.email) {
2011
+ return { valid: false, error: 'Email required' }
2012
+ }
2013
+ if (!user.name) {
2014
+ return { valid: false, error: 'Name required' }
2015
+ }
2016
+ }
2017
+
2018
+ return { valid: true }
2019
+ }
2020
+ ```
2021
+
2022
+ ---
2023
+
2024
+ ### Rule: js-hoist-regexp
2025
+
2026
+ ---
2027
+ title: Hoist RegExp Creation
2028
+ impact: LOW-MEDIUM
2029
+ impactDescription: avoids recreation
2030
+ tags: javascript, regexp, optimization, memoization
2031
+ ---
2032
+
2033
+ ## Hoist RegExp Creation
2034
+
2035
+ Don't create RegExp inside render. Hoist to module scope or memoize with `useMemo()`.
2036
+
2037
+ **Incorrect (new RegExp every render):**
2038
+
2039
+ ```tsx
2040
+ function Highlighter({ text, query }: Props) {
2041
+ const regex = new RegExp(`(${query})`, 'gi')
2042
+ const parts = text.split(regex)
2043
+ return <>{parts.map((part, i) => ...)}</>
2044
+ }
2045
+ ```
2046
+
2047
+ **Correct (memoize or hoist):**
2048
+
2049
+ ```tsx
2050
+ const EMAIL_REGEX = /^[^\s@]+@[^\s@]+\.[^\s@]+$/
2051
+
2052
+ function Highlighter({ text, query }: Props) {
2053
+ const regex = useMemo(
2054
+ () => new RegExp(`(${escapeRegex(query)})`, 'gi'),
2055
+ [query]
2056
+ )
2057
+ const parts = text.split(regex)
2058
+ return <>{parts.map((part, i) => ...)}</>
2059
+ }
2060
+ ```
2061
+
2062
+ **Warning (global regex has mutable state):**
2063
+
2064
+ Global regex (`/g`) has mutable `lastIndex` state:
2065
+
2066
+ ```typescript
2067
+ const regex = /foo/g
2068
+ regex.test('foo') // true, lastIndex = 3
2069
+ regex.test('foo') // false, lastIndex = 0
2070
+ ```
2071
+
2072
+ ---
2073
+
2074
+ ### Rule: js-index-maps
2075
+
2076
+ ---
2077
+ title: Build Index Maps for Repeated Lookups
2078
+ impact: LOW-MEDIUM
2079
+ impactDescription: 1M ops to 2K ops
2080
+ tags: javascript, map, indexing, optimization, performance
2081
+ ---
2082
+
2083
+ ## Build Index Maps for Repeated Lookups
2084
+
2085
+ Multiple `.find()` calls by the same key should use a Map.
2086
+
2087
+ **Incorrect (O(n) per lookup):**
2088
+
2089
+ ```typescript
2090
+ function processOrders(orders: Order[], users: User[]) {
2091
+ return orders.map(order => ({
2092
+ ...order,
2093
+ user: users.find(u => u.id === order.userId)
2094
+ }))
2095
+ }
2096
+ ```
2097
+
2098
+ **Correct (O(1) per lookup):**
2099
+
2100
+ ```typescript
2101
+ function processOrders(orders: Order[], users: User[]) {
2102
+ const userById = new Map(users.map(u => [u.id, u]))
2103
+
2104
+ return orders.map(order => ({
2105
+ ...order,
2106
+ user: userById.get(order.userId)
2107
+ }))
2108
+ }
2109
+ ```
2110
+
2111
+ Build map once (O(n)), then all lookups are O(1).
2112
+ For 1000 orders × 1000 users: 1M ops → 2K ops.
2113
+
2114
+ ---
2115
+
2116
+ ### Rule: js-length-check-first
2117
+
2118
+ ---
2119
+ title: Early Length Check for Array Comparisons
2120
+ impact: MEDIUM-HIGH
2121
+ impactDescription: avoids expensive operations when lengths differ
2122
+ tags: javascript, arrays, performance, optimization, comparison
2123
+ ---
2124
+
2125
+ ## Early Length Check for Array Comparisons
2126
+
2127
+ When comparing arrays with expensive operations (sorting, deep equality, serialization), check lengths first. If lengths differ, the arrays cannot be equal.
2128
+
2129
+ In real-world applications, this optimization is especially valuable when the comparison runs in hot paths (event handlers, render loops).
2130
+
2131
+ **Incorrect (always runs expensive comparison):**
2132
+
2133
+ ```typescript
2134
+ function hasChanges(current: string[], original: string[]) {
2135
+ // Always sorts and joins, even when lengths differ
2136
+ return current.sort().join() !== original.sort().join()
2137
+ }
2138
+ ```
2139
+
2140
+ Two O(n log n) sorts run even when `current.length` is 5 and `original.length` is 100. There is also overhead of joining the arrays and comparing the strings.
2141
+
2142
+ **Correct (O(1) length check first):**
2143
+
2144
+ ```typescript
2145
+ function hasChanges(current: string[], original: string[]) {
2146
+ // Early return if lengths differ
2147
+ if (current.length !== original.length) {
2148
+ return true
2149
+ }
2150
+ // Only sort when lengths match
2151
+ const currentSorted = current.toSorted()
2152
+ const originalSorted = original.toSorted()
2153
+ for (let i = 0; i < currentSorted.length; i++) {
2154
+ if (currentSorted[i] !== originalSorted[i]) {
2155
+ return true
2156
+ }
2157
+ }
2158
+ return false
2159
+ }
2160
+ ```
2161
+
2162
+ This new approach is more efficient because:
2163
+ - It avoids the overhead of sorting and joining the arrays when lengths differ
2164
+ - It avoids consuming memory for the joined strings (especially important for large arrays)
2165
+ - It avoids mutating the original arrays
2166
+ - It returns early when a difference is found
2167
+
2168
+ ---
2169
+
2170
+ ### Rule: js-min-max-loop
2171
+
2172
+ ---
2173
+ title: Use Loop for Min/Max Instead of Sort
2174
+ impact: LOW
2175
+ impactDescription: O(n) instead of O(n log n)
2176
+ tags: javascript, arrays, performance, sorting, algorithms
2177
+ ---
2178
+
2179
+ ## Use Loop for Min/Max Instead of Sort
2180
+
2181
+ Finding the smallest or largest element only requires a single pass through the array. Sorting is wasteful and slower.
2182
+
2183
+ **Incorrect (O(n log n) - sort to find latest):**
2184
+
2185
+ ```typescript
2186
+ interface Project {
2187
+ id: string
2188
+ name: string
2189
+ updatedAt: number
2190
+ }
2191
+
2192
+ function getLatestProject(projects: Project[]) {
2193
+ const sorted = [...projects].sort((a, b) => b.updatedAt - a.updatedAt)
2194
+ return sorted[0]
2195
+ }
2196
+ ```
2197
+
2198
+ Sorts the entire array just to find the maximum value.
2199
+
2200
+ **Incorrect (O(n log n) - sort for oldest and newest):**
2201
+
2202
+ ```typescript
2203
+ function getOldestAndNewest(projects: Project[]) {
2204
+ const sorted = [...projects].sort((a, b) => a.updatedAt - b.updatedAt)
2205
+ return { oldest: sorted[0], newest: sorted[sorted.length - 1] }
2206
+ }
2207
+ ```
2208
+
2209
+ Still sorts unnecessarily when only min/max are needed.
2210
+
2211
+ **Correct (O(n) - single loop):**
2212
+
2213
+ ```typescript
2214
+ function getLatestProject(projects: Project[]) {
2215
+ if (projects.length === 0) return null
2216
+
2217
+ let latest = projects[0]
2218
+
2219
+ for (let i = 1; i < projects.length; i++) {
2220
+ if (projects[i].updatedAt > latest.updatedAt) {
2221
+ latest = projects[i]
2222
+ }
2223
+ }
2224
+
2225
+ return latest
2226
+ }
2227
+
2228
+ function getOldestAndNewest(projects: Project[]) {
2229
+ if (projects.length === 0) return { oldest: null, newest: null }
2230
+
2231
+ let oldest = projects[0]
2232
+ let newest = projects[0]
2233
+
2234
+ for (let i = 1; i < projects.length; i++) {
2235
+ if (projects[i].updatedAt < oldest.updatedAt) oldest = projects[i]
2236
+ if (projects[i].updatedAt > newest.updatedAt) newest = projects[i]
2237
+ }
2238
+
2239
+ return { oldest, newest }
2240
+ }
2241
+ ```
2242
+
2243
+ Single pass through the array, no copying, no sorting.
2244
+
2245
+ **Alternative (Math.min/Math.max for small arrays):**
2246
+
2247
+ ```typescript
2248
+ const numbers = [5, 2, 8, 1, 9]
2249
+ const min = Math.min(...numbers)
2250
+ const max = Math.max(...numbers)
2251
+ ```
2252
+
2253
+ This works for small arrays, but can be slower or just throw an error for very large arrays due to spread operator limitations. Maximal array length is approximately 124000 in Chrome 143 and 638000 in Safari 18; exact numbers may vary - see [the fiddle](https://jsfiddle.net/qw1jabsx/4/). Use the loop approach for reliability.
2254
+
2255
+ ---
2256
+
2257
+ ### Rule: js-set-map-lookups
2258
+
2259
+ ---
2260
+ title: Use Set/Map for O(1) Lookups
2261
+ impact: LOW-MEDIUM
2262
+ impactDescription: O(n) to O(1)
2263
+ tags: javascript, set, map, data-structures, performance
2264
+ ---
2265
+
2266
+ ## Use Set/Map for O(1) Lookups
2267
+
2268
+ Convert arrays to Set/Map for repeated membership checks.
2269
+
2270
+ **Incorrect (O(n) per check):**
2271
+
2272
+ ```typescript
2273
+ const allowedIds = ['a', 'b', 'c', ...]
2274
+ items.filter(item => allowedIds.includes(item.id))
2275
+ ```
2276
+
2277
+ **Correct (O(1) per check):**
2278
+
2279
+ ```typescript
2280
+ const allowedIds = new Set(['a', 'b', 'c', ...])
2281
+ items.filter(item => allowedIds.has(item.id))
2282
+ ```
2283
+
2284
+ ---
2285
+
2286
+ ### Rule: js-tosorted-immutable
2287
+
2288
+ ---
2289
+ title: Use toSorted() Instead of sort() for Immutability
2290
+ impact: MEDIUM-HIGH
2291
+ impactDescription: prevents mutation bugs in React state
2292
+ tags: javascript, arrays, immutability, react, state, mutation
2293
+ ---
2294
+
2295
+ ## Use toSorted() Instead of sort() for Immutability
2296
+
2297
+ `.sort()` mutates the array in place, which can cause bugs with React state and props. Use `.toSorted()` to create a new sorted array without mutation.
2298
+
2299
+ **Incorrect (mutates original array):**
2300
+
2301
+ ```typescript
2302
+ function UserList({ users }: { users: User[] }) {
2303
+ // Mutates the users prop array!
2304
+ const sorted = useMemo(
2305
+ () => users.sort((a, b) => a.name.localeCompare(b.name)),
2306
+ [users]
2307
+ )
2308
+ return <div>{sorted.map(renderUser)}</div>
2309
+ }
2310
+ ```
2311
+
2312
+ **Correct (creates new array):**
2313
+
2314
+ ```typescript
2315
+ function UserList({ users }: { users: User[] }) {
2316
+ // Creates new sorted array, original unchanged
2317
+ const sorted = useMemo(
2318
+ () => users.toSorted((a, b) => a.name.localeCompare(b.name)),
2319
+ [users]
2320
+ )
2321
+ return <div>{sorted.map(renderUser)}</div>
2322
+ }
2323
+ ```
2324
+
2325
+ **Why this matters in React:**
2326
+
2327
+ 1. Props/state mutations break React's immutability model - React expects props and state to be treated as read-only
2328
+ 2. Causes stale closure bugs - Mutating arrays inside closures (callbacks, effects) can lead to unexpected behavior
2329
+
2330
+ **Browser support (fallback for older browsers):**
2331
+
2332
+ `.toSorted()` is available in all modern browsers (Chrome 110+, Safari 16+, Firefox 115+, Node.js 20+). For older environments, use spread operator:
2333
+
2334
+ ```typescript
2335
+ // Fallback for older browsers
2336
+ const sorted = [...items].sort((a, b) => a.value - b.value)
2337
+ ```
2338
+
2339
+ **Other immutable array methods:**
2340
+
2341
+ - `.toSorted()` - immutable sort
2342
+ - `.toReversed()` - immutable reverse
2343
+ - `.toSpliced()` - immutable splice
2344
+ - `.with()` - immutable element replacement
2345
+
2346
+ ---
2347
+
2348
+ ### Rule: rendering-activity
2349
+
2350
+ ---
2351
+ title: Use Activity Component for Show/Hide
2352
+ impact: MEDIUM
2353
+ impactDescription: preserves state/DOM
2354
+ tags: rendering, activity, visibility, state-preservation
2355
+ ---
2356
+
2357
+ ## Use Activity Component for Show/Hide
2358
+
2359
+ Use React's `<Activity>` to preserve state/DOM for expensive components that frequently toggle visibility.
2360
+
2361
+ **Usage:**
2362
+
2363
+ ```tsx
2364
+ import { Activity } from 'react'
2365
+
2366
+ function Dropdown({ isOpen }: Props) {
2367
+ return (
2368
+ <Activity mode={isOpen ? 'visible' : 'hidden'}>
2369
+ <ExpensiveMenu />
2370
+ </Activity>
2371
+ )
2372
+ }
2373
+ ```
2374
+
2375
+ Avoids expensive re-renders and state loss.
2376
+
2377
+ ---
2378
+
2379
+ ### Rule: rendering-animate-svg-wrapper
2380
+
2381
+ ---
2382
+ title: Animate SVG Wrapper Instead of SVG Element
2383
+ impact: LOW
2384
+ impactDescription: enables hardware acceleration
2385
+ tags: rendering, svg, css, animation, performance
2386
+ ---
2387
+
2388
+ ## Animate SVG Wrapper Instead of SVG Element
2389
+
2390
+ Many browsers don't have hardware acceleration for CSS3 animations on SVG elements. Wrap SVG in a `<div>` and animate the wrapper instead.
2391
+
2392
+ **Incorrect (animating SVG directly - no hardware acceleration):**
2393
+
2394
+ ```tsx
2395
+ function LoadingSpinner() {
2396
+ return (
2397
+ <svg
2398
+ className="animate-spin"
2399
+ width="24"
2400
+ height="24"
2401
+ viewBox="0 0 24 24"
2402
+ >
2403
+ <circle cx="12" cy="12" r="10" stroke="currentColor" />
2404
+ </svg>
2405
+ )
2406
+ }
2407
+ ```
2408
+
2409
+ **Correct (animating wrapper div - hardware accelerated):**
2410
+
2411
+ ```tsx
2412
+ function LoadingSpinner() {
2413
+ return (
2414
+ <div className="animate-spin">
2415
+ <svg
2416
+ width="24"
2417
+ height="24"
2418
+ viewBox="0 0 24 24"
2419
+ >
2420
+ <circle cx="12" cy="12" r="10" stroke="currentColor" />
2421
+ </svg>
2422
+ </div>
2423
+ )
2424
+ }
2425
+ ```
2426
+
2427
+ This applies to all CSS transforms and transitions (`transform`, `opacity`, `translate`, `scale`, `rotate`). The wrapper div allows browsers to use GPU acceleration for smoother animations.
2428
+
2429
+ ---
2430
+
2431
+ ### Rule: rendering-conditional-render
2432
+
2433
+ ---
2434
+ title: Use Explicit Conditional Rendering
2435
+ impact: LOW
2436
+ impactDescription: prevents rendering 0 or NaN
2437
+ tags: rendering, conditional, jsx, falsy-values
2438
+ ---
2439
+
2440
+ ## Use Explicit Conditional Rendering
2441
+
2442
+ Use explicit ternary operators (`? :`) instead of `&&` for conditional rendering when the condition can be `0`, `NaN`, or other falsy values that render.
2443
+
2444
+ **Incorrect (renders "0" when count is 0):**
2445
+
2446
+ ```tsx
2447
+ function Badge({ count }: { count: number }) {
2448
+ return (
2449
+ <div>
2450
+ {count && <span className="badge">{count}</span>}
2451
+ </div>
2452
+ )
2453
+ }
2454
+
2455
+ // When count = 0, renders: <div>0</div>
2456
+ // When count = 5, renders: <div><span class="badge">5</span></div>
2457
+ ```
2458
+
2459
+ **Correct (renders nothing when count is 0):**
2460
+
2461
+ ```tsx
2462
+ function Badge({ count }: { count: number }) {
2463
+ return (
2464
+ <div>
2465
+ {count > 0 ? <span className="badge">{count}</span> : null}
2466
+ </div>
2467
+ )
2468
+ }
2469
+
2470
+ // When count = 0, renders: <div></div>
2471
+ // When count = 5, renders: <div><span class="badge">5</span></div>
2472
+ ```
2473
+
2474
+ ---
2475
+
2476
+ ### Rule: rendering-content-visibility
2477
+
2478
+ ---
2479
+ title: CSS content-visibility for Long Lists
2480
+ impact: HIGH
2481
+ impactDescription: faster initial render
2482
+ tags: rendering, css, content-visibility, long-lists
2483
+ ---
2484
+
2485
+ ## CSS content-visibility for Long Lists
2486
+
2487
+ Apply `content-visibility: auto` to defer off-screen rendering.
2488
+
2489
+ **CSS:**
2490
+
2491
+ ```css
2492
+ .message-item {
2493
+ content-visibility: auto;
2494
+ contain-intrinsic-size: 0 80px;
2495
+ }
2496
+ ```
2497
+
2498
+ **Example:**
2499
+
2500
+ ```tsx
2501
+ function MessageList({ messages }: { messages: Message[] }) {
2502
+ return (
2503
+ <div className="overflow-y-auto h-screen">
2504
+ {messages.map(msg => (
2505
+ <div key={msg.id} className="message-item">
2506
+ <Avatar user={msg.author} />
2507
+ <div>{msg.content}</div>
2508
+ </div>
2509
+ ))}
2510
+ </div>
2511
+ )
2512
+ }
2513
+ ```
2514
+
2515
+ For 1000 messages, browser skips layout/paint for ~990 off-screen items (10× faster initial render).
2516
+
2517
+ ---
2518
+
2519
+ ### Rule: rendering-hoist-jsx
2520
+
2521
+ ---
2522
+ title: Hoist Static JSX Elements
2523
+ impact: LOW
2524
+ impactDescription: avoids re-creation
2525
+ tags: rendering, jsx, static, optimization
2526
+ ---
2527
+
2528
+ ## Hoist Static JSX Elements
2529
+
2530
+ Extract static JSX outside components to avoid re-creation.
2531
+
2532
+ **Incorrect (recreates element every render):**
2533
+
2534
+ ```tsx
2535
+ function LoadingSkeleton() {
2536
+ return <div className="animate-pulse h-20 bg-gray-200" />
2537
+ }
2538
+
2539
+ function Container() {
2540
+ return (
2541
+ <div>
2542
+ {loading && <LoadingSkeleton />}
2543
+ </div>
2544
+ )
2545
+ }
2546
+ ```
2547
+
2548
+ **Correct (reuses same element):**
2549
+
2550
+ ```tsx
2551
+ const loadingSkeleton = (
2552
+ <div className="animate-pulse h-20 bg-gray-200" />
2553
+ )
2554
+
2555
+ function Container() {
2556
+ return (
2557
+ <div>
2558
+ {loading && loadingSkeleton}
2559
+ </div>
2560
+ )
2561
+ }
2562
+ ```
2563
+
2564
+ This is especially helpful for large and static SVG nodes, which can be expensive to recreate on every render.
2565
+
2566
+ **Note:** If your project has [React Compiler](https://react.dev/learn/react-compiler) enabled, the compiler automatically hoists static JSX elements and optimizes component re-renders, making manual hoisting unnecessary.
2567
+
2568
+ ---
2569
+
2570
+ ### Rule: rendering-hydration-no-flicker
2571
+
2572
+ ---
2573
+ title: Prevent Hydration Mismatch Without Flickering
2574
+ impact: MEDIUM
2575
+ impactDescription: avoids visual flicker and hydration errors
2576
+ tags: rendering, ssr, hydration, localStorage, flicker
2577
+ ---
2578
+
2579
+ ## Prevent Hydration Mismatch Without Flickering
2580
+
2581
+ When rendering content that depends on client-side storage (localStorage, cookies), avoid both SSR breakage and post-hydration flickering by injecting a synchronous script that updates the DOM before React hydrates.
2582
+
2583
+ **Incorrect (breaks SSR):**
2584
+
2585
+ ```tsx
2586
+ function ThemeWrapper({ children }: { children: ReactNode }) {
2587
+ // localStorage is not available on server - throws error
2588
+ const theme = localStorage.getItem('theme') || 'light'
2589
+
2590
+ return (
2591
+ <div className={theme}>
2592
+ {children}
2593
+ </div>
2594
+ )
2595
+ }
2596
+ ```
2597
+
2598
+ Server-side rendering will fail because `localStorage` is undefined.
2599
+
2600
+ **Incorrect (visual flickering):**
2601
+
2602
+ ```tsx
2603
+ function ThemeWrapper({ children }: { children: ReactNode }) {
2604
+ const [theme, setTheme] = useState('light')
2605
+
2606
+ useEffect(() => {
2607
+ // Runs after hydration - causes visible flash
2608
+ const stored = localStorage.getItem('theme')
2609
+ if (stored) {
2610
+ setTheme(stored)
2611
+ }
2612
+ }, [])
2613
+
2614
+ return (
2615
+ <div className={theme}>
2616
+ {children}
2617
+ </div>
2618
+ )
2619
+ }
2620
+ ```
2621
+
2622
+ Component first renders with default value (`light`), then updates after hydration, causing a visible flash of incorrect content.
2623
+
2624
+ **Correct (no flicker, no hydration mismatch):**
2625
+
2626
+ ```tsx
2627
+ function ThemeWrapper({ children }: { children: ReactNode }) {
2628
+ return (
2629
+ <>
2630
+ <div id="theme-wrapper">
2631
+ {children}
2632
+ </div>
2633
+ <script
2634
+ dangerouslySetInnerHTML={{
2635
+ __html: `
2636
+ (function() {
2637
+ try {
2638
+ var theme = localStorage.getItem('theme') || 'light';
2639
+ var el = document.getElementById('theme-wrapper');
2640
+ if (el) el.className = theme;
2641
+ } catch (e) {}
2642
+ })();
2643
+ `,
2644
+ }}
2645
+ />
2646
+ </>
2647
+ )
2648
+ }
2649
+ ```
2650
+
2651
+ The inline script executes synchronously before showing the element, ensuring the DOM already has the correct value. No flickering, no hydration mismatch.
2652
+
2653
+ This pattern is especially useful for theme toggles, user preferences, authentication states, and any client-only data that should render immediately without flashing default values.
2654
+
2655
+ ---
2656
+
2657
+ ### Rule: rendering-hydration-suppress-warning
2658
+
2659
+ ---
2660
+ title: Suppress Expected Hydration Mismatches
2661
+ impact: LOW-MEDIUM
2662
+ impactDescription: avoids noisy hydration warnings for known differences
2663
+ tags: rendering, hydration, ssr, nextjs
2664
+ ---
2665
+
2666
+ ## Suppress Expected Hydration Mismatches
2667
+
2668
+ In SSR frameworks (e.g., Next.js), some values are intentionally different on server vs client (random IDs, dates, locale/timezone formatting). For these *expected* mismatches, wrap the dynamic text in an element with `suppressHydrationWarning` to prevent noisy warnings. Do not use this to hide real bugs. Don’t overuse it.
2669
+
2670
+ **Incorrect (known mismatch warnings):**
2671
+
2672
+ ```tsx
2673
+ function Timestamp() {
2674
+ return <span>{new Date().toLocaleString()}</span>
2675
+ }
2676
+ ```
2677
+
2678
+ **Correct (suppress expected mismatch only):**
2679
+
2680
+ ```tsx
2681
+ function Timestamp() {
2682
+ return (
2683
+ <span suppressHydrationWarning>
2684
+ {new Date().toLocaleString()}
2685
+ </span>
2686
+ )
2687
+ }
2688
+ ```
2689
+
2690
+ ---
2691
+
2692
+ ### Rule: rendering-svg-precision
2693
+
2694
+ ---
2695
+ title: Optimize SVG Precision
2696
+ impact: LOW
2697
+ impactDescription: reduces file size
2698
+ tags: rendering, svg, optimization, svgo
2699
+ ---
2700
+
2701
+ ## Optimize SVG Precision
2702
+
2703
+ Reduce SVG coordinate precision to decrease file size. The optimal precision depends on the viewBox size, but in general reducing precision should be considered.
2704
+
2705
+ **Incorrect (excessive precision):**
2706
+
2707
+ ```svg
2708
+ <path d="M 10.293847 20.847362 L 30.938472 40.192837" />
2709
+ ```
2710
+
2711
+ **Correct (1 decimal place):**
2712
+
2713
+ ```svg
2714
+ <path d="M 10.3 20.8 L 30.9 40.2" />
2715
+ ```
2716
+
2717
+ **Automate with SVGO:**
2718
+
2719
+ ```bash
2720
+ npx svgo --precision=1 --multipass icon.svg
2721
+ ```
2722
+
2723
+ ---
2724
+
2725
+ ### Rule: rendering-usetransition-loading
2726
+
2727
+ ---
2728
+ title: Use useTransition Over Manual Loading States
2729
+ impact: LOW
2730
+ impactDescription: reduces re-renders and improves code clarity
2731
+ tags: rendering, transitions, useTransition, loading, state
2732
+ ---
2733
+
2734
+ ## Use useTransition Over Manual Loading States
2735
+
2736
+ Use `useTransition` instead of manual `useState` for loading states. This provides built-in `isPending` state and automatically manages transitions.
2737
+
2738
+ **Incorrect (manual loading state):**
2739
+
2740
+ ```tsx
2741
+ function SearchResults() {
2742
+ const [query, setQuery] = useState('')
2743
+ const [results, setResults] = useState([])
2744
+ const [isLoading, setIsLoading] = useState(false)
2745
+
2746
+ const handleSearch = async (value: string) => {
2747
+ setIsLoading(true)
2748
+ setQuery(value)
2749
+ const data = await fetchResults(value)
2750
+ setResults(data)
2751
+ setIsLoading(false)
2752
+ }
2753
+
2754
+ return (
2755
+ <>
2756
+ <input onChange={(e) => handleSearch(e.target.value)} />
2757
+ {isLoading && <Spinner />}
2758
+ <ResultsList results={results} />
2759
+ </>
2760
+ )
2761
+ }
2762
+ ```
2763
+
2764
+ **Correct (useTransition with built-in pending state):**
2765
+
2766
+ ```tsx
2767
+ import { useTransition, useState } from 'react'
2768
+
2769
+ function SearchResults() {
2770
+ const [query, setQuery] = useState('')
2771
+ const [results, setResults] = useState([])
2772
+ const [isPending, startTransition] = useTransition()
2773
+
2774
+ const handleSearch = (value: string) => {
2775
+ setQuery(value) // Update input immediately
2776
+
2777
+ startTransition(async () => {
2778
+ // Fetch and update results
2779
+ const data = await fetchResults(value)
2780
+ setResults(data)
2781
+ })
2782
+ }
2783
+
2784
+ return (
2785
+ <>
2786
+ <input onChange={(e) => handleSearch(e.target.value)} />
2787
+ {isPending && <Spinner />}
2788
+ <ResultsList results={results} />
2789
+ </>
2790
+ )
2791
+ }
2792
+ ```
2793
+
2794
+ **Benefits:**
2795
+
2796
+ - **Automatic pending state**: No need to manually manage `setIsLoading(true/false)`
2797
+ - **Error resilience**: Pending state correctly resets even if the transition throws
2798
+ - **Better responsiveness**: Keeps the UI responsive during updates
2799
+ - **Interrupt handling**: New transitions automatically cancel pending ones
2800
+
2801
+ Reference: [useTransition](https://react.dev/reference/react/useTransition)
2802
+
2803
+ ---
2804
+
2805
+ ### Rule: rerender-defer-reads
2806
+
2807
+ ---
2808
+ title: Defer State Reads to Usage Point
2809
+ impact: MEDIUM
2810
+ impactDescription: avoids unnecessary subscriptions
2811
+ tags: rerender, searchParams, localStorage, optimization
2812
+ ---
2813
+
2814
+ ## Defer State Reads to Usage Point
2815
+
2816
+ Don't subscribe to dynamic state (searchParams, localStorage) if you only read it inside callbacks.
2817
+
2818
+ **Incorrect (subscribes to all searchParams changes):**
2819
+
2820
+ ```tsx
2821
+ function ShareButton({ chatId }: { chatId: string }) {
2822
+ const searchParams = useSearchParams()
2823
+
2824
+ const handleShare = () => {
2825
+ const ref = searchParams.get('ref')
2826
+ shareChat(chatId, { ref })
2827
+ }
2828
+
2829
+ return <button onClick={handleShare}>Share</button>
2830
+ }
2831
+ ```
2832
+
2833
+ **Correct (reads on demand, no subscription):**
2834
+
2835
+ ```tsx
2836
+ function ShareButton({ chatId }: { chatId: string }) {
2837
+ const handleShare = () => {
2838
+ const params = new URLSearchParams(window.location.search)
2839
+ const ref = params.get('ref')
2840
+ shareChat(chatId, { ref })
2841
+ }
2842
+
2843
+ return <button onClick={handleShare}>Share</button>
2844
+ }
2845
+ ```
2846
+
2847
+ ---
2848
+
2849
+ ### Rule: rerender-dependencies
2850
+
2851
+ ---
2852
+ title: Narrow Effect Dependencies
2853
+ impact: LOW
2854
+ impactDescription: minimizes effect re-runs
2855
+ tags: rerender, useEffect, dependencies, optimization
2856
+ ---
2857
+
2858
+ ## Narrow Effect Dependencies
2859
+
2860
+ Specify primitive dependencies instead of objects to minimize effect re-runs.
2861
+
2862
+ **Incorrect (re-runs on any user field change):**
2863
+
2864
+ ```tsx
2865
+ useEffect(() => {
2866
+ console.log(user.id)
2867
+ }, [user])
2868
+ ```
2869
+
2870
+ **Correct (re-runs only when id changes):**
2871
+
2872
+ ```tsx
2873
+ useEffect(() => {
2874
+ console.log(user.id)
2875
+ }, [user.id])
2876
+ ```
2877
+
2878
+ **For derived state, compute outside effect:**
2879
+
2880
+ ```tsx
2881
+ // Incorrect: runs on width=767, 766, 765...
2882
+ useEffect(() => {
2883
+ if (width < 768) {
2884
+ enableMobileMode()
2885
+ }
2886
+ }, [width])
2887
+
2888
+ // Correct: runs only on boolean transition
2889
+ const isMobile = width < 768
2890
+ useEffect(() => {
2891
+ if (isMobile) {
2892
+ enableMobileMode()
2893
+ }
2894
+ }, [isMobile])
2895
+ ```
2896
+
2897
+ ---
2898
+
2899
+ ### Rule: rerender-derived-state
2900
+
2901
+ ---
2902
+ title: Subscribe to Derived State
2903
+ impact: MEDIUM
2904
+ impactDescription: reduces re-render frequency
2905
+ tags: rerender, derived-state, media-query, optimization
2906
+ ---
2907
+
2908
+ ## Subscribe to Derived State
2909
+
2910
+ Subscribe to derived boolean state instead of continuous values to reduce re-render frequency.
2911
+
2912
+ **Incorrect (re-renders on every pixel change):**
2913
+
2914
+ ```tsx
2915
+ function Sidebar() {
2916
+ const width = useWindowWidth() // updates continuously
2917
+ const isMobile = width < 768
2918
+ return <nav className={isMobile ? 'mobile' : 'desktop'} />
2919
+ }
2920
+ ```
2921
+
2922
+ **Correct (re-renders only when boolean changes):**
2923
+
2924
+ ```tsx
2925
+ function Sidebar() {
2926
+ const isMobile = useMediaQuery('(max-width: 767px)')
2927
+ return <nav className={isMobile ? 'mobile' : 'desktop'} />
2928
+ }
2929
+ ```
2930
+
2931
+ ---
2932
+
2933
+ ### Rule: rerender-derived-state-no-effect
2934
+
2935
+ ---
2936
+ title: Calculate Derived State During Rendering
2937
+ impact: MEDIUM
2938
+ impactDescription: avoids redundant renders and state drift
2939
+ tags: rerender, derived-state, useEffect, state
2940
+ ---
2941
+
2942
+ ## Calculate Derived State During Rendering
2943
+
2944
+ If a value can be computed from current props/state, do not store it in state or update it in an effect. Derive it during render to avoid extra renders and state drift. Do not set state in effects solely in response to prop changes; prefer derived values or keyed resets instead.
2945
+
2946
+ **Incorrect (redundant state and effect):**
2947
+
2948
+ ```tsx
2949
+ function Form() {
2950
+ const [firstName, setFirstName] = useState('First')
2951
+ const [lastName, setLastName] = useState('Last')
2952
+ const [fullName, setFullName] = useState('')
2953
+
2954
+ useEffect(() => {
2955
+ setFullName(firstName + ' ' + lastName)
2956
+ }, [firstName, lastName])
2957
+
2958
+ return <p>{fullName}</p>
2959
+ }
2960
+ ```
2961
+
2962
+ **Correct (derive during render):**
2963
+
2964
+ ```tsx
2965
+ function Form() {
2966
+ const [firstName, setFirstName] = useState('First')
2967
+ const [lastName, setLastName] = useState('Last')
2968
+ const fullName = firstName + ' ' + lastName
2969
+
2970
+ return <p>{fullName}</p>
2971
+ }
2972
+ ```
2973
+
2974
+ References: [You Might Not Need an Effect](https://react.dev/learn/you-might-not-need-an-effect)
2975
+
2976
+ ---
2977
+
2978
+ ### Rule: rerender-functional-setstate
2979
+
2980
+ ---
2981
+ title: Use Functional setState Updates
2982
+ impact: MEDIUM
2983
+ impactDescription: prevents stale closures and unnecessary callback recreations
2984
+ tags: react, hooks, useState, useCallback, callbacks, closures
2985
+ ---
2986
+
2987
+ ## Use Functional setState Updates
2988
+
2989
+ When updating state based on the current state value, use the functional update form of setState instead of directly referencing the state variable. This prevents stale closures, eliminates unnecessary dependencies, and creates stable callback references.
2990
+
2991
+ **Incorrect (requires state as dependency):**
2992
+
2993
+ ```tsx
2994
+ function TodoList() {
2995
+ const [items, setItems] = useState(initialItems)
2996
+
2997
+ // Callback must depend on items, recreated on every items change
2998
+ const addItems = useCallback((newItems: Item[]) => {
2999
+ setItems([...items, ...newItems])
3000
+ }, [items]) // ❌ items dependency causes recreations
3001
+
3002
+ // Risk of stale closure if dependency is forgotten
3003
+ const removeItem = useCallback((id: string) => {
3004
+ setItems(items.filter(item => item.id !== id))
3005
+ }, []) // ❌ Missing items dependency - will use stale items!
3006
+
3007
+ return <ItemsEditor items={items} onAdd={addItems} onRemove={removeItem} />
3008
+ }
3009
+ ```
3010
+
3011
+ The first callback is recreated every time `items` changes, which can cause child components to re-render unnecessarily. The second callback has a stale closure bug—it will always reference the initial `items` value.
3012
+
3013
+ **Correct (stable callbacks, no stale closures):**
3014
+
3015
+ ```tsx
3016
+ function TodoList() {
3017
+ const [items, setItems] = useState(initialItems)
3018
+
3019
+ // Stable callback, never recreated
3020
+ const addItems = useCallback((newItems: Item[]) => {
3021
+ setItems(curr => [...curr, ...newItems])
3022
+ }, []) // ✅ No dependencies needed
3023
+
3024
+ // Always uses latest state, no stale closure risk
3025
+ const removeItem = useCallback((id: string) => {
3026
+ setItems(curr => curr.filter(item => item.id !== id))
3027
+ }, []) // ✅ Safe and stable
3028
+
3029
+ return <ItemsEditor items={items} onAdd={addItems} onRemove={removeItem} />
3030
+ }
3031
+ ```
3032
+
3033
+ **Benefits:**
3034
+
3035
+ 1. **Stable callback references** - Callbacks don't need to be recreated when state changes
3036
+ 2. **No stale closures** - Always operates on the latest state value
3037
+ 3. **Fewer dependencies** - Simplifies dependency arrays and reduces memory leaks
3038
+ 4. **Prevents bugs** - Eliminates the most common source of React closure bugs
3039
+
3040
+ **When to use functional updates:**
3041
+
3042
+ - Any setState that depends on the current state value
3043
+ - Inside useCallback/useMemo when state is needed
3044
+ - Event handlers that reference state
3045
+ - Async operations that update state
3046
+
3047
+ **When direct updates are fine:**
3048
+
3049
+ - Setting state to a static value: `setCount(0)`
3050
+ - Setting state from props/arguments only: `setName(newName)`
3051
+ - State doesn't depend on previous value
3052
+
3053
+ **Note:** If your project has [React Compiler](https://react.dev/learn/react-compiler) enabled, the compiler can automatically optimize some cases, but functional updates are still recommended for correctness and to prevent stale closure bugs.
3054
+
3055
+ ---
3056
+
3057
+ ### Rule: rerender-lazy-state-init
3058
+
3059
+ ---
3060
+ title: Use Lazy State Initialization
3061
+ impact: MEDIUM
3062
+ impactDescription: wasted computation on every render
3063
+ tags: react, hooks, useState, performance, initialization
3064
+ ---
3065
+
3066
+ ## Use Lazy State Initialization
3067
+
3068
+ Pass a function to `useState` for expensive initial values. Without the function form, the initializer runs on every render even though the value is only used once.
3069
+
3070
+ **Incorrect (runs on every render):**
3071
+
3072
+ ```tsx
3073
+ function FilteredList({ items }: { items: Item[] }) {
3074
+ // buildSearchIndex() runs on EVERY render, even after initialization
3075
+ const [searchIndex, setSearchIndex] = useState(buildSearchIndex(items))
3076
+ const [query, setQuery] = useState('')
3077
+
3078
+ // When query changes, buildSearchIndex runs again unnecessarily
3079
+ return <SearchResults index={searchIndex} query={query} />
3080
+ }
3081
+
3082
+ function UserProfile() {
3083
+ // JSON.parse runs on every render
3084
+ const [settings, setSettings] = useState(
3085
+ JSON.parse(localStorage.getItem('settings') || '{}')
3086
+ )
3087
+
3088
+ return <SettingsForm settings={settings} onChange={setSettings} />
3089
+ }
3090
+ ```
3091
+
3092
+ **Correct (runs only once):**
3093
+
3094
+ ```tsx
3095
+ function FilteredList({ items }: { items: Item[] }) {
3096
+ // buildSearchIndex() runs ONLY on initial render
3097
+ const [searchIndex, setSearchIndex] = useState(() => buildSearchIndex(items))
3098
+ const [query, setQuery] = useState('')
3099
+
3100
+ return <SearchResults index={searchIndex} query={query} />
3101
+ }
3102
+
3103
+ function UserProfile() {
3104
+ // JSON.parse runs only on initial render
3105
+ const [settings, setSettings] = useState(() => {
3106
+ const stored = localStorage.getItem('settings')
3107
+ return stored ? JSON.parse(stored) : {}
3108
+ })
3109
+
3110
+ return <SettingsForm settings={settings} onChange={setSettings} />
3111
+ }
3112
+ ```
3113
+
3114
+ Use lazy initialization when computing initial values from localStorage/sessionStorage, building data structures (indexes, maps), reading from the DOM, or performing heavy transformations.
3115
+
3116
+ For simple primitives (`useState(0)`), direct references (`useState(props.value)`), or cheap literals (`useState({})`), the function form is unnecessary.
3117
+
3118
+ ---
3119
+
3120
+ ### Rule: rerender-memo
3121
+
3122
+ ---
3123
+ title: Extract to Memoized Components
3124
+ impact: MEDIUM
3125
+ impactDescription: enables early returns
3126
+ tags: rerender, memo, useMemo, optimization
3127
+ ---
3128
+
3129
+ ## Extract to Memoized Components
3130
+
3131
+ Extract expensive work into memoized components to enable early returns before computation.
3132
+
3133
+ **Incorrect (computes avatar even when loading):**
3134
+
3135
+ ```tsx
3136
+ function Profile({ user, loading }: Props) {
3137
+ const avatar = useMemo(() => {
3138
+ const id = computeAvatarId(user)
3139
+ return <Avatar id={id} />
3140
+ }, [user])
3141
+
3142
+ if (loading) return <Skeleton />
3143
+ return <div>{avatar}</div>
3144
+ }
3145
+ ```
3146
+
3147
+ **Correct (skips computation when loading):**
3148
+
3149
+ ```tsx
3150
+ const UserAvatar = memo(function UserAvatar({ user }: { user: User }) {
3151
+ const id = useMemo(() => computeAvatarId(user), [user])
3152
+ return <Avatar id={id} />
3153
+ })
3154
+
3155
+ function Profile({ user, loading }: Props) {
3156
+ if (loading) return <Skeleton />
3157
+ return (
3158
+ <div>
3159
+ <UserAvatar user={user} />
3160
+ </div>
3161
+ )
3162
+ }
3163
+ ```
3164
+
3165
+ **Note:** If your project has [React Compiler](https://react.dev/learn/react-compiler) enabled, manual memoization with `memo()` and `useMemo()` is not necessary. The compiler automatically optimizes re-renders.
3166
+
3167
+ ---
3168
+
3169
+ ### Rule: rerender-memo-with-default-value
3170
+
3171
+ ---
3172
+
3173
+ title: Extract Default Non-primitive Parameter Value from Memoized Component to Constant
3174
+ impact: MEDIUM
3175
+ impactDescription: restores memoization by using a constant for default value
3176
+ tags: rerender, memo, optimization
3177
+
3178
+ ---
3179
+
3180
+ ## Extract Default Non-primitive Parameter Value from Memoized Component to Constant
3181
+
3182
+ When memoized component has a default value for some non-primitive optional parameter, such as an array, function, or object, calling the component without that parameter results in broken memoization. This is because new value instances are created on every rerender, and they do not pass strict equality comparison in `memo()`.
3183
+
3184
+ To address this issue, extract the default value into a constant.
3185
+
3186
+ **Incorrect (`onClick` has different values on every rerender):**
3187
+
3188
+ ```tsx
3189
+ const UserAvatar = memo(function UserAvatar({ onClick = () => {} }: { onClick?: () => void }) {
3190
+ // ...
3191
+ })
3192
+
3193
+ // Used without optional onClick
3194
+ <UserAvatar />
3195
+ ```
3196
+
3197
+ **Correct (stable default value):**
3198
+
3199
+ ```tsx
3200
+ const NOOP = () => {};
3201
+
3202
+ const UserAvatar = memo(function UserAvatar({ onClick = NOOP }: { onClick?: () => void }) {
3203
+ // ...
3204
+ })
3205
+
3206
+ // Used without optional onClick
3207
+ <UserAvatar />
3208
+ ```
3209
+
3210
+ ---
3211
+
3212
+ ### Rule: rerender-move-effect-to-event
3213
+
3214
+ ---
3215
+ title: Put Interaction Logic in Event Handlers
3216
+ impact: MEDIUM
3217
+ impactDescription: avoids effect re-runs and duplicate side effects
3218
+ tags: rerender, useEffect, events, side-effects, dependencies
3219
+ ---
3220
+
3221
+ ## Put Interaction Logic in Event Handlers
3222
+
3223
+ If a side effect is triggered by a specific user action (submit, click, drag), run it in that event handler. Do not model the action as state + effect; it makes effects re-run on unrelated changes and can duplicate the action.
3224
+
3225
+ **Incorrect (event modeled as state + effect):**
3226
+
3227
+ ```tsx
3228
+ function Form() {
3229
+ const [submitted, setSubmitted] = useState(false)
3230
+ const theme = useContext(ThemeContext)
3231
+
3232
+ useEffect(() => {
3233
+ if (submitted) {
3234
+ post('/api/register')
3235
+ showToast('Registered', theme)
3236
+ }
3237
+ }, [submitted, theme])
3238
+
3239
+ return <button onClick={() => setSubmitted(true)}>Submit</button>
3240
+ }
3241
+ ```
3242
+
3243
+ **Correct (do it in the handler):**
3244
+
3245
+ ```tsx
3246
+ function Form() {
3247
+ const theme = useContext(ThemeContext)
3248
+
3249
+ function handleSubmit() {
3250
+ post('/api/register')
3251
+ showToast('Registered', theme)
3252
+ }
3253
+
3254
+ return <button onClick={handleSubmit}>Submit</button>
3255
+ }
3256
+ ```
3257
+
3258
+ Reference: [Should this code move to an event handler?](https://react.dev/learn/removing-effect-dependencies#should-this-code-move-to-an-event-handler)
3259
+
3260
+ ---
3261
+
3262
+ ### Rule: rerender-simple-expression-in-memo
3263
+
3264
+ ---
3265
+ title: Do not wrap a simple expression with a primitive result type in useMemo
3266
+ impact: LOW-MEDIUM
3267
+ impactDescription: wasted computation on every render
3268
+ tags: rerender, useMemo, optimization
3269
+ ---
3270
+
3271
+ ## Do not wrap a simple expression with a primitive result type in useMemo
3272
+
3273
+ When an expression is simple (few logical or arithmetical operators) and has a primitive result type (boolean, number, string), do not wrap it in `useMemo`.
3274
+ Calling `useMemo` and comparing hook dependencies may consume more resources than the expression itself.
3275
+
3276
+ **Incorrect:**
3277
+
3278
+ ```tsx
3279
+ function Header({ user, notifications }: Props) {
3280
+ const isLoading = useMemo(() => {
3281
+ return user.isLoading || notifications.isLoading
3282
+ }, [user.isLoading, notifications.isLoading])
3283
+
3284
+ if (isLoading) return <Skeleton />
3285
+ // return some markup
3286
+ }
3287
+ ```
3288
+
3289
+ **Correct:**
3290
+
3291
+ ```tsx
3292
+ function Header({ user, notifications }: Props) {
3293
+ const isLoading = user.isLoading || notifications.isLoading
3294
+
3295
+ if (isLoading) return <Skeleton />
3296
+ // return some markup
3297
+ }
3298
+ ```
3299
+
3300
+ ---
3301
+
3302
+ ### Rule: rerender-transitions
3303
+
3304
+ ---
3305
+ title: Use Transitions for Non-Urgent Updates
3306
+ impact: MEDIUM
3307
+ impactDescription: maintains UI responsiveness
3308
+ tags: rerender, transitions, startTransition, performance
3309
+ ---
3310
+
3311
+ ## Use Transitions for Non-Urgent Updates
3312
+
3313
+ Mark frequent, non-urgent state updates as transitions to maintain UI responsiveness.
3314
+
3315
+ **Incorrect (blocks UI on every scroll):**
3316
+
3317
+ ```tsx
3318
+ function ScrollTracker() {
3319
+ const [scrollY, setScrollY] = useState(0)
3320
+ useEffect(() => {
3321
+ const handler = () => setScrollY(window.scrollY)
3322
+ window.addEventListener('scroll', handler, { passive: true })
3323
+ return () => window.removeEventListener('scroll', handler)
3324
+ }, [])
3325
+ }
3326
+ ```
3327
+
3328
+ **Correct (non-blocking updates):**
3329
+
3330
+ ```tsx
3331
+ import { startTransition } from 'react'
3332
+
3333
+ function ScrollTracker() {
3334
+ const [scrollY, setScrollY] = useState(0)
3335
+ useEffect(() => {
3336
+ const handler = () => {
3337
+ startTransition(() => setScrollY(window.scrollY))
3338
+ }
3339
+ window.addEventListener('scroll', handler, { passive: true })
3340
+ return () => window.removeEventListener('scroll', handler)
3341
+ }, [])
3342
+ }
3343
+ ```
3344
+
3345
+ ---
3346
+
3347
+ ### Rule: rerender-use-ref-transient-values
3348
+
3349
+ ---
3350
+ title: Use useRef for Transient Values
3351
+ impact: MEDIUM
3352
+ impactDescription: avoids unnecessary re-renders on frequent updates
3353
+ tags: rerender, useref, state, performance
3354
+ ---
3355
+
3356
+ ## Use useRef for Transient Values
3357
+
3358
+ When a value changes frequently and you don't want a re-render on every update (e.g., mouse trackers, intervals, transient flags), store it in `useRef` instead of `useState`. Keep component state for UI; use refs for temporary DOM-adjacent values. Updating a ref does not trigger a re-render.
3359
+
3360
+ **Incorrect (renders every update):**
3361
+
3362
+ ```tsx
3363
+ function Tracker() {
3364
+ const [lastX, setLastX] = useState(0)
3365
+
3366
+ useEffect(() => {
3367
+ const onMove = (e: MouseEvent) => setLastX(e.clientX)
3368
+ window.addEventListener('mousemove', onMove)
3369
+ return () => window.removeEventListener('mousemove', onMove)
3370
+ }, [])
3371
+
3372
+ return (
3373
+ <div
3374
+ style={{
3375
+ position: 'fixed',
3376
+ top: 0,
3377
+ left: lastX,
3378
+ width: 8,
3379
+ height: 8,
3380
+ background: 'black',
3381
+ }}
3382
+ />
3383
+ )
3384
+ }
3385
+ ```
3386
+
3387
+ **Correct (no re-render for tracking):**
3388
+
3389
+ ```tsx
3390
+ function Tracker() {
3391
+ const lastXRef = useRef(0)
3392
+ const dotRef = useRef<HTMLDivElement>(null)
3393
+
3394
+ useEffect(() => {
3395
+ const onMove = (e: MouseEvent) => {
3396
+ lastXRef.current = e.clientX
3397
+ const node = dotRef.current
3398
+ if (node) {
3399
+ node.style.transform = `translateX(${e.clientX}px)`
3400
+ }
3401
+ }
3402
+ window.addEventListener('mousemove', onMove)
3403
+ return () => window.removeEventListener('mousemove', onMove)
3404
+ }, [])
3405
+
3406
+ return (
3407
+ <div
3408
+ ref={dotRef}
3409
+ style={{
3410
+ position: 'fixed',
3411
+ top: 0,
3412
+ left: 0,
3413
+ width: 8,
3414
+ height: 8,
3415
+ background: 'black',
3416
+ transform: 'translateX(0px)',
3417
+ }}
3418
+ />
3419
+ )
3420
+ }
3421
+ ```
3422
+
3423
+ ---
3424
+
3425
+ ### Rule: server-after-nonblocking
3426
+
3427
+ ---
3428
+ title: Use after() for Non-Blocking Operations
3429
+ impact: MEDIUM
3430
+ impactDescription: faster response times
3431
+ tags: server, async, logging, analytics, side-effects
3432
+ ---
3433
+
3434
+ ## Use after() for Non-Blocking Operations
3435
+
3436
+ Use Next.js's `after()` to schedule work that should execute after a response is sent. This prevents logging, analytics, and other side effects from blocking the response.
3437
+
3438
+ **Incorrect (blocks response):**
3439
+
3440
+ ```tsx
3441
+ import { logUserAction } from '@/app/utils'
3442
+
3443
+ export async function POST(request: Request) {
3444
+ // Perform mutation
3445
+ await updateDatabase(request)
3446
+
3447
+ // Logging blocks the response
3448
+ const userAgent = request.headers.get('user-agent') || 'unknown'
3449
+ await logUserAction({ userAgent })
3450
+
3451
+ return new Response(JSON.stringify({ status: 'success' }), {
3452
+ status: 200,
3453
+ headers: { 'Content-Type': 'application/json' }
3454
+ })
3455
+ }
3456
+ ```
3457
+
3458
+ **Correct (non-blocking):**
3459
+
3460
+ ```tsx
3461
+ import { after } from 'next/server'
3462
+ import { headers, cookies } from 'next/headers'
3463
+ import { logUserAction } from '@/app/utils'
3464
+
3465
+ export async function POST(request: Request) {
3466
+ // Perform mutation
3467
+ await updateDatabase(request)
3468
+
3469
+ // Log after response is sent
3470
+ after(async () => {
3471
+ const userAgent = (await headers()).get('user-agent') || 'unknown'
3472
+ const sessionCookie = (await cookies()).get('session-id')?.value || 'anonymous'
3473
+
3474
+ logUserAction({ sessionCookie, userAgent })
3475
+ })
3476
+
3477
+ return new Response(JSON.stringify({ status: 'success' }), {
3478
+ status: 200,
3479
+ headers: { 'Content-Type': 'application/json' }
3480
+ })
3481
+ }
3482
+ ```
3483
+
3484
+ The response is sent immediately while logging happens in the background.
3485
+
3486
+ **Common use cases:**
3487
+
3488
+ - Analytics tracking
3489
+ - Audit logging
3490
+ - Sending notifications
3491
+ - Cache invalidation
3492
+ - Cleanup tasks
3493
+
3494
+ **Important notes:**
3495
+
3496
+ - `after()` runs even if the response fails or redirects
3497
+ - Works in Server Actions, Route Handlers, and Server Components
3498
+
3499
+ Reference: [https://nextjs.org/docs/app/api-reference/functions/after](https://nextjs.org/docs/app/api-reference/functions/after)
3500
+
3501
+ ---
3502
+
3503
+ ### Rule: server-auth-actions
3504
+
3505
+ ---
3506
+ title: Authenticate Server Actions Like API Routes
3507
+ impact: CRITICAL
3508
+ impactDescription: prevents unauthorized access to server mutations
3509
+ tags: server, server-actions, authentication, security, authorization
3510
+ ---
3511
+
3512
+ ## Authenticate Server Actions Like API Routes
3513
+
3514
+ **Impact: CRITICAL (prevents unauthorized access to server mutations)**
3515
+
3516
+ Server Actions (functions with `"use server"`) are exposed as public endpoints, just like API routes. Always verify authentication and authorization **inside** each Server Action—do not rely solely on middleware, layout guards, or page-level checks, as Server Actions can be invoked directly.
3517
+
3518
+ Next.js documentation explicitly states: "Treat Server Actions with the same security considerations as public-facing API endpoints, and verify if the user is allowed to perform a mutation."
3519
+
3520
+ **Incorrect (no authentication check):**
3521
+
3522
+ ```typescript
3523
+ 'use server'
3524
+
3525
+ export async function deleteUser(userId: string) {
3526
+ // Anyone can call this! No auth check
3527
+ await db.user.delete({ where: { id: userId } })
3528
+ return { success: true }
3529
+ }
3530
+ ```
3531
+
3532
+ **Correct (authentication inside the action):**
3533
+
3534
+ ```typescript
3535
+ 'use server'
3536
+
3537
+ import { verifySession } from '@/lib/auth'
3538
+ import { unauthorized } from '@/lib/errors'
3539
+
3540
+ export async function deleteUser(userId: string) {
3541
+ // Always check auth inside the action
3542
+ const session = await verifySession()
3543
+
3544
+ if (!session) {
3545
+ throw unauthorized('Must be logged in')
3546
+ }
3547
+
3548
+ // Check authorization too
3549
+ if (session.user.role !== 'admin' && session.user.id !== userId) {
3550
+ throw unauthorized('Cannot delete other users')
3551
+ }
3552
+
3553
+ await db.user.delete({ where: { id: userId } })
3554
+ return { success: true }
3555
+ }
3556
+ ```
3557
+
3558
+ **With input validation:**
3559
+
3560
+ ```typescript
3561
+ 'use server'
3562
+
3563
+ import { verifySession } from '@/lib/auth'
3564
+ import { z } from 'zod'
3565
+
3566
+ const updateProfileSchema = z.object({
3567
+ userId: z.string().uuid(),
3568
+ name: z.string().min(1).max(100),
3569
+ email: z.string().email()
3570
+ })
3571
+
3572
+ export async function updateProfile(data: unknown) {
3573
+ // Validate input first
3574
+ const validated = updateProfileSchema.parse(data)
3575
+
3576
+ // Then authenticate
3577
+ const session = await verifySession()
3578
+ if (!session) {
3579
+ throw new Error('Unauthorized')
3580
+ }
3581
+
3582
+ // Then authorize
3583
+ if (session.user.id !== validated.userId) {
3584
+ throw new Error('Can only update own profile')
3585
+ }
3586
+
3587
+ // Finally perform the mutation
3588
+ await db.user.update({
3589
+ where: { id: validated.userId },
3590
+ data: {
3591
+ name: validated.name,
3592
+ email: validated.email
3593
+ }
3594
+ })
3595
+
3596
+ return { success: true }
3597
+ }
3598
+ ```
3599
+
3600
+ Reference: [https://nextjs.org/docs/app/guides/authentication](https://nextjs.org/docs/app/guides/authentication)
3601
+
3602
+ ---
3603
+
3604
+ ### Rule: server-cache-lru
3605
+
3606
+ ---
3607
+ title: Cross-Request LRU Caching
3608
+ impact: HIGH
3609
+ impactDescription: caches across requests
3610
+ tags: server, cache, lru, cross-request
3611
+ ---
3612
+
3613
+ ## Cross-Request LRU Caching
3614
+
3615
+ `React.cache()` only works within one request. For data shared across sequential requests (user clicks button A then button B), use an LRU cache.
3616
+
3617
+ **Implementation:**
3618
+
3619
+ ```typescript
3620
+ import { LRUCache } from 'lru-cache'
3621
+
3622
+ const cache = new LRUCache<string, any>({
3623
+ max: 1000,
3624
+ ttl: 5 * 60 * 1000 // 5 minutes
3625
+ })
3626
+
3627
+ export async function getUser(id: string) {
3628
+ const cached = cache.get(id)
3629
+ if (cached) return cached
3630
+
3631
+ const user = await db.user.findUnique({ where: { id } })
3632
+ cache.set(id, user)
3633
+ return user
3634
+ }
3635
+
3636
+ // Request 1: DB query, result cached
3637
+ // Request 2: cache hit, no DB query
3638
+ ```
3639
+
3640
+ Use when sequential user actions hit multiple endpoints needing the same data within seconds.
3641
+
3642
+ **With Vercel's [Fluid Compute](https://vercel.com/docs/fluid-compute):** LRU caching is especially effective because multiple concurrent requests can share the same function instance and cache. This means the cache persists across requests without needing external storage like Redis.
3643
+
3644
+ **In traditional serverless:** Each invocation runs in isolation, so consider Redis for cross-process caching.
3645
+
3646
+ Reference: [https://github.com/isaacs/node-lru-cache](https://github.com/isaacs/node-lru-cache)
3647
+
3648
+ ---
3649
+
3650
+ ### Rule: server-cache-react
3651
+
3652
+ ---
3653
+ title: Per-Request Deduplication with React.cache()
3654
+ impact: MEDIUM
3655
+ impactDescription: deduplicates within request
3656
+ tags: server, cache, react-cache, deduplication
3657
+ ---
3658
+
3659
+ ## Per-Request Deduplication with React.cache()
3660
+
3661
+ Use `React.cache()` for server-side request deduplication. Authentication and database queries benefit most.
3662
+
3663
+ **Usage:**
3664
+
3665
+ ```typescript
3666
+ import { cache } from 'react'
3667
+
3668
+ export const getCurrentUser = cache(async () => {
3669
+ const session = await auth()
3670
+ if (!session?.user?.id) return null
3671
+ return await db.user.findUnique({
3672
+ where: { id: session.user.id }
3673
+ })
3674
+ })
3675
+ ```
3676
+
3677
+ Within a single request, multiple calls to `getCurrentUser()` execute the query only once.
3678
+
3679
+ **Avoid inline objects as arguments:**
3680
+
3681
+ `React.cache()` uses shallow equality (`Object.is`) to determine cache hits. Inline objects create new references each call, preventing cache hits.
3682
+
3683
+ **Incorrect (always cache miss):**
3684
+
3685
+ ```typescript
3686
+ const getUser = cache(async (params: { uid: number }) => {
3687
+ return await db.user.findUnique({ where: { id: params.uid } })
3688
+ })
3689
+
3690
+ // Each call creates new object, never hits cache
3691
+ getUser({ uid: 1 })
3692
+ getUser({ uid: 1 }) // Cache miss, runs query again
3693
+ ```
3694
+
3695
+ **Correct (cache hit):**
3696
+
3697
+ ```typescript
3698
+ const getUser = cache(async (uid: number) => {
3699
+ return await db.user.findUnique({ where: { id: uid } })
3700
+ })
3701
+
3702
+ // Primitive args use value equality
3703
+ getUser(1)
3704
+ getUser(1) // Cache hit, returns cached result
3705
+ ```
3706
+
3707
+ If you must pass objects, pass the same reference:
3708
+
3709
+ ```typescript
3710
+ const params = { uid: 1 }
3711
+ getUser(params) // Query runs
3712
+ getUser(params) // Cache hit (same reference)
3713
+ ```
3714
+
3715
+ **Next.js-Specific Note:**
3716
+
3717
+ In Next.js, the `fetch` API is automatically extended with request memoization. Requests with the same URL and options are automatically deduplicated within a single request, so you don't need `React.cache()` for `fetch` calls. However, `React.cache()` is still essential for other async tasks:
3718
+
3719
+ - Database queries (Prisma, Drizzle, etc.)
3720
+ - Heavy computations
3721
+ - Authentication checks
3722
+ - File system operations
3723
+ - Any non-fetch async work
3724
+
3725
+ Use `React.cache()` to deduplicate these operations across your component tree.
3726
+
3727
+ Reference: [React.cache documentation](https://react.dev/reference/react/cache)
3728
+
3729
+ ---
3730
+
3731
+ ### Rule: server-dedup-props
3732
+
3733
+ ---
3734
+ title: Avoid Duplicate Serialization in RSC Props
3735
+ impact: LOW
3736
+ impactDescription: reduces network payload by avoiding duplicate serialization
3737
+ tags: server, rsc, serialization, props, client-components
3738
+ ---
3739
+
3740
+ ## Avoid Duplicate Serialization in RSC Props
3741
+
3742
+ **Impact: LOW (reduces network payload by avoiding duplicate serialization)**
3743
+
3744
+ RSC→client serialization deduplicates by object reference, not value. Same reference = serialized once; new reference = serialized again. Do transformations (`.toSorted()`, `.filter()`, `.map()`) in client, not server.
3745
+
3746
+ **Incorrect (duplicates array):**
3747
+
3748
+ ```tsx
3749
+ // RSC: sends 6 strings (2 arrays × 3 items)
3750
+ <ClientList usernames={usernames} usernamesOrdered={usernames.toSorted()} />
3751
+ ```
3752
+
3753
+ **Correct (sends 3 strings):**
3754
+
3755
+ ```tsx
3756
+ // RSC: send once
3757
+ <ClientList usernames={usernames} />
3758
+
3759
+ // Client: transform there
3760
+ 'use client'
3761
+ const sorted = useMemo(() => [...usernames].sort(), [usernames])
3762
+ ```
3763
+
3764
+ **Nested deduplication behavior:**
3765
+
3766
+ Deduplication works recursively. Impact varies by data type:
3767
+
3768
+ - `string[]`, `number[]`, `boolean[]`: **HIGH impact** - array + all primitives fully duplicated
3769
+ - `object[]`: **LOW impact** - array duplicated, but nested objects deduplicated by reference
3770
+
3771
+ ```tsx
3772
+ // string[] - duplicates everything
3773
+ usernames={['a','b']} sorted={usernames.toSorted()} // sends 4 strings
3774
+
3775
+ // object[] - duplicates array structure only
3776
+ users={[{id:1},{id:2}]} sorted={users.toSorted()} // sends 2 arrays + 2 unique objects (not 4)
3777
+ ```
3778
+
3779
+ **Operations breaking deduplication (create new references):**
3780
+
3781
+ - Arrays: `.toSorted()`, `.filter()`, `.map()`, `.slice()`, `[...arr]`
3782
+ - Objects: `{...obj}`, `Object.assign()`, `structuredClone()`, `JSON.parse(JSON.stringify())`
3783
+
3784
+ **More examples:**
3785
+
3786
+ ```tsx
3787
+ // ⠌ Bad
3788
+ <C users={users} active={users.filter(u => u.active)} />
3789
+ <C product={product} productName={product.name} />
3790
+
3791
+ // ✅ Good
3792
+ <C users={users} />
3793
+ <C product={product} />
3794
+ // Do filtering/destructuring in client
3795
+ ```
3796
+
3797
+ **Exception:** Pass derived data when transformation is expensive or client doesn't need original.
3798
+
3799
+ ---
3800
+
3801
+ ### Rule: server-parallel-fetching
3802
+
3803
+ ---
3804
+ title: Parallel Data Fetching with Component Composition
3805
+ impact: CRITICAL
3806
+ impactDescription: eliminates server-side waterfalls
3807
+ tags: server, rsc, parallel-fetching, composition
3808
+ ---
3809
+
3810
+ ## Parallel Data Fetching with Component Composition
3811
+
3812
+ React Server Components execute sequentially within a tree. Restructure with composition to parallelize data fetching.
3813
+
3814
+ **Incorrect (Sidebar waits for Page's fetch to complete):**
3815
+
3816
+ ```tsx
3817
+ export default async function Page() {
3818
+ const header = await fetchHeader()
3819
+ return (
3820
+ <div>
3821
+ <div>{header}</div>
3822
+ <Sidebar />
3823
+ </div>
3824
+ )
3825
+ }
3826
+
3827
+ async function Sidebar() {
3828
+ const items = await fetchSidebarItems()
3829
+ return <nav>{items.map(renderItem)}</nav>
3830
+ }
3831
+ ```
3832
+
3833
+ **Correct (both fetch simultaneously):**
3834
+
3835
+ ```tsx
3836
+ async function Header() {
3837
+ const data = await fetchHeader()
3838
+ return <div>{data}</div>
3839
+ }
3840
+
3841
+ async function Sidebar() {
3842
+ const items = await fetchSidebarItems()
3843
+ return <nav>{items.map(renderItem)}</nav>
3844
+ }
3845
+
3846
+ export default function Page() {
3847
+ return (
3848
+ <div>
3849
+ <Header />
3850
+ <Sidebar />
3851
+ </div>
3852
+ )
3853
+ }
3854
+ ```
3855
+
3856
+ **Alternative with children prop:**
3857
+
3858
+ ```tsx
3859
+ async function Header() {
3860
+ const data = await fetchHeader()
3861
+ return <div>{data}</div>
3862
+ }
3863
+
3864
+ async function Sidebar() {
3865
+ const items = await fetchSidebarItems()
3866
+ return <nav>{items.map(renderItem)}</nav>
3867
+ }
3868
+
3869
+ function Layout({ children }: { children: ReactNode }) {
3870
+ return (
3871
+ <div>
3872
+ <Header />
3873
+ {children}
3874
+ </div>
3875
+ )
3876
+ }
3877
+
3878
+ export default function Page() {
3879
+ return (
3880
+ <Layout>
3881
+ <Sidebar />
3882
+ </Layout>
3883
+ )
3884
+ }
3885
+ ```
3886
+
3887
+ ---
3888
+
3889
+ ### Rule: server-serialization
3890
+
3891
+ ---
3892
+ title: Minimize Serialization at RSC Boundaries
3893
+ impact: HIGH
3894
+ impactDescription: reduces data transfer size
3895
+ tags: server, rsc, serialization, props
3896
+ ---
3897
+
3898
+ ## Minimize Serialization at RSC Boundaries
3899
+
3900
+ The React Server/Client boundary serializes all object properties into strings and embeds them in the HTML response and subsequent RSC requests. This serialized data directly impacts page weight and load time, so **size matters a lot**. Only pass fields that the client actually uses.
3901
+
3902
+ **Incorrect (serializes all 50 fields):**
3903
+
3904
+ ```tsx
3905
+ async function Page() {
3906
+ const user = await fetchUser() // 50 fields
3907
+ return <Profile user={user} />
3908
+ }
3909
+
3910
+ 'use client'
3911
+ function Profile({ user }: { user: User }) {
3912
+ return <div>{user.name}</div> // uses 1 field
3913
+ }
3914
+ ```
3915
+
3916
+ **Correct (serializes only 1 field):**
3917
+
3918
+ ```tsx
3919
+ async function Page() {
3920
+ const user = await fetchUser()
3921
+ return <Profile name={user.name} />
3922
+ }
3923
+
3924
+ 'use client'
3925
+ function Profile({ name }: { name: string }) {
3926
+ return <div>{name}</div>
3927
+ }
3928
+ ```
3929
+
3930
+ ---
3931
+
3932
+ ⚡ PikaKit v3.9.134