@pythoughts/vue-skills-mcp 0.1.0

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 (208) hide show
  1. package/README.md +63 -0
  2. package/index.mjs +139 -0
  3. package/package.json +34 -0
  4. package/skills/create-adaptable-composable/SKILL.md +76 -0
  5. package/skills/vue-best-practices/SKILL.md +154 -0
  6. package/skills/vue-best-practices/references/animation-class-based-technique.md +254 -0
  7. package/skills/vue-best-practices/references/animation-state-driven-technique.md +291 -0
  8. package/skills/vue-best-practices/references/component-async.md +97 -0
  9. package/skills/vue-best-practices/references/component-data-flow.md +350 -0
  10. package/skills/vue-best-practices/references/component-fallthrough-attrs.md +174 -0
  11. package/skills/vue-best-practices/references/component-keep-alive.md +137 -0
  12. package/skills/vue-best-practices/references/component-slots.md +216 -0
  13. package/skills/vue-best-practices/references/component-suspense.md +228 -0
  14. package/skills/vue-best-practices/references/component-teleport.md +108 -0
  15. package/skills/vue-best-practices/references/component-transition-group.md +128 -0
  16. package/skills/vue-best-practices/references/component-transition.md +125 -0
  17. package/skills/vue-best-practices/references/composables.md +290 -0
  18. package/skills/vue-best-practices/references/directives.md +162 -0
  19. package/skills/vue-best-practices/references/perf-avoid-component-abstraction-in-lists.md +159 -0
  20. package/skills/vue-best-practices/references/perf-v-once-v-memo-directives.md +182 -0
  21. package/skills/vue-best-practices/references/perf-virtualize-large-lists.md +187 -0
  22. package/skills/vue-best-practices/references/plugins.md +166 -0
  23. package/skills/vue-best-practices/references/reactivity.md +346 -0
  24. package/skills/vue-best-practices/references/render-functions.md +201 -0
  25. package/skills/vue-best-practices/references/sfc.md +310 -0
  26. package/skills/vue-best-practices/references/state-management.md +135 -0
  27. package/skills/vue-best-practices/references/updated-hook-performance.md +187 -0
  28. package/skills/vue-debug-guides/SKILL.md +205 -0
  29. package/skills/vue-debug-guides/reference/animation-key-for-rerender.md +160 -0
  30. package/skills/vue-debug-guides/reference/animation-transitiongroup-performance.md +241 -0
  31. package/skills/vue-debug-guides/reference/async-component-error-handling.md +115 -0
  32. package/skills/vue-debug-guides/reference/async-component-keepalive-ref-issue.md +112 -0
  33. package/skills/vue-debug-guides/reference/async-component-suspense-control.md +84 -0
  34. package/skills/vue-debug-guides/reference/async-component-vue-router.md +109 -0
  35. package/skills/vue-debug-guides/reference/attrs-event-listener-merging.md +205 -0
  36. package/skills/vue-debug-guides/reference/checkbox-true-false-value-form-submission.md +118 -0
  37. package/skills/vue-debug-guides/reference/cleanup-side-effects.md +172 -0
  38. package/skills/vue-debug-guides/reference/click-events-on-components.md +180 -0
  39. package/skills/vue-debug-guides/reference/component-naming-conflicts.md +159 -0
  40. package/skills/vue-debug-guides/reference/component-ref-requires-defineexpose.md +176 -0
  41. package/skills/vue-debug-guides/reference/composable-avoid-hidden-side-effects.md +208 -0
  42. package/skills/vue-debug-guides/reference/composable-call-location-restrictions.md +141 -0
  43. package/skills/vue-debug-guides/reference/composable-naming-return-pattern.md +139 -0
  44. package/skills/vue-debug-guides/reference/composable-tovalue-inside-watcheffect.md +182 -0
  45. package/skills/vue-debug-guides/reference/composition-api-not-functional-programming.md +120 -0
  46. package/skills/vue-debug-guides/reference/composition-api-script-setup-async-context.md +203 -0
  47. package/skills/vue-debug-guides/reference/composition-api-vs-react-hooks-differences.md +156 -0
  48. package/skills/vue-debug-guides/reference/computed-array-mutation.md +148 -0
  49. package/skills/vue-debug-guides/reference/computed-conditional-dependencies.md +147 -0
  50. package/skills/vue-debug-guides/reference/computed-no-parameters.md +159 -0
  51. package/skills/vue-debug-guides/reference/computed-no-side-effects.md +107 -0
  52. package/skills/vue-debug-guides/reference/computed-return-value-readonly.md +160 -0
  53. package/skills/vue-debug-guides/reference/configure-app-before-mount.md +89 -0
  54. package/skills/vue-debug-guides/reference/declare-emits-for-documentation.md +212 -0
  55. package/skills/vue-debug-guides/reference/define-expose-before-await.md +192 -0
  56. package/skills/vue-debug-guides/reference/define-model-default-value-sync.md +139 -0
  57. package/skills/vue-debug-guides/reference/defineEmits-must-be-top-level.md +164 -0
  58. package/skills/vue-debug-guides/reference/defineEmits-no-runtime-and-type-mixed.md +170 -0
  59. package/skills/vue-debug-guides/reference/definemodel-object-mutation-no-emit.md +148 -0
  60. package/skills/vue-debug-guides/reference/dom-update-timing-nexttick.md +90 -0
  61. package/skills/vue-debug-guides/reference/dynamic-argument-constraints.md +146 -0
  62. package/skills/vue-debug-guides/reference/dynamic-component-registration-vite.md +147 -0
  63. package/skills/vue-debug-guides/reference/event-modifier-order-matters.md +101 -0
  64. package/skills/vue-debug-guides/reference/exact-modifier-for-precise-shortcuts.md +155 -0
  65. package/skills/vue-debug-guides/reference/fallthrough-attrs-overwrite-vue3.md +159 -0
  66. package/skills/vue-debug-guides/reference/in-dom-template-parsing-caveats.md +149 -0
  67. package/skills/vue-debug-guides/reference/inheritattrs-false-for-wrapper-components.md +230 -0
  68. package/skills/vue-debug-guides/reference/keepalive-router-nested-double-mount.md +222 -0
  69. package/skills/vue-debug-guides/reference/keepalive-transition-memory-leak.md +144 -0
  70. package/skills/vue-debug-guides/reference/keyup-modifier-timing.md +137 -0
  71. package/skills/vue-debug-guides/reference/lifecycle-dom-access-timing.md +216 -0
  72. package/skills/vue-debug-guides/reference/lifecycle-hooks-synchronous-registration.md +156 -0
  73. package/skills/vue-debug-guides/reference/lifecycle-ssr-awareness.md +184 -0
  74. package/skills/vue-debug-guides/reference/local-components-not-in-descendants.md +151 -0
  75. package/skills/vue-debug-guides/reference/mount-return-value.md +88 -0
  76. package/skills/vue-debug-guides/reference/multi-root-component-class-attrs.md +93 -0
  77. package/skills/vue-debug-guides/reference/native-event-collision-with-emits.md +162 -0
  78. package/skills/vue-debug-guides/reference/no-passive-with-prevent.md +141 -0
  79. package/skills/vue-debug-guides/reference/no-v-if-with-v-for.md +136 -0
  80. package/skills/vue-debug-guides/reference/perf-computed-object-stability.md +157 -0
  81. package/skills/vue-debug-guides/reference/perf-props-stability-update-optimization.md +140 -0
  82. package/skills/vue-debug-guides/reference/plugin-global-properties-sparingly.md +109 -0
  83. package/skills/vue-debug-guides/reference/plugin-install-before-mount.md +124 -0
  84. package/skills/vue-debug-guides/reference/plugin-prefer-provide-inject-over-global-properties.md +120 -0
  85. package/skills/vue-debug-guides/reference/plugin-typescript-type-augmentation.md +157 -0
  86. package/skills/vue-debug-guides/reference/prop-defineprops-scope-limitation.md +161 -0
  87. package/skills/vue-debug-guides/reference/provide-inject-debugging-challenges.md +203 -0
  88. package/skills/vue-debug-guides/reference/provide-inject-default-value-factory.md +244 -0
  89. package/skills/vue-debug-guides/reference/provide-inject-reactivity-not-automatic.md +226 -0
  90. package/skills/vue-debug-guides/reference/provide-inject-synchronous-setup.md +235 -0
  91. package/skills/vue-debug-guides/reference/reactive-destructuring.md +89 -0
  92. package/skills/vue-debug-guides/reference/reactivity-debugging-hooks.md +132 -0
  93. package/skills/vue-debug-guides/reference/reactivity-markraw-for-non-reactive.md +149 -0
  94. package/skills/vue-debug-guides/reference/reactivity-proxy-identity-hazard.md +96 -0
  95. package/skills/vue-debug-guides/reference/reactivity-same-tick-batching.md +166 -0
  96. package/skills/vue-debug-guides/reference/ref-value-access.md +61 -0
  97. package/skills/vue-debug-guides/reference/refs-in-collections-need-value.md +81 -0
  98. package/skills/vue-debug-guides/reference/render-function-avoid-internal-vnode-properties.md +151 -0
  99. package/skills/vue-debug-guides/reference/render-function-vnodes-must-be-unique.md +133 -0
  100. package/skills/vue-debug-guides/reference/rendering-render-function-h-import-vue3.md +148 -0
  101. package/skills/vue-debug-guides/reference/rendering-render-function-return-from-setup.md +148 -0
  102. package/skills/vue-debug-guides/reference/rendering-render-function-slots-as-functions.md +168 -0
  103. package/skills/vue-debug-guides/reference/rendering-resolve-component-for-string-names.md +231 -0
  104. package/skills/vue-debug-guides/reference/select-initial-value-ios-bug.md +91 -0
  105. package/skills/vue-debug-guides/reference/self-referencing-component-name.md +157 -0
  106. package/skills/vue-debug-guides/reference/sfc-named-exports-forbidden.md +184 -0
  107. package/skills/vue-debug-guides/reference/sfc-scoped-css-child-component-styling.md +156 -0
  108. package/skills/vue-debug-guides/reference/sfc-scoped-css-dynamic-content.md +193 -0
  109. package/skills/vue-debug-guides/reference/sfc-scoped-css-slot-content.md +242 -0
  110. package/skills/vue-debug-guides/reference/sfc-script-setup-reactivity.md +195 -0
  111. package/skills/vue-debug-guides/reference/slot-forwarding-to-child-components.md +143 -0
  112. package/skills/vue-debug-guides/reference/slot-implicit-default-content.md +155 -0
  113. package/skills/vue-debug-guides/reference/slot-name-reserved-prop.md +109 -0
  114. package/skills/vue-debug-guides/reference/slot-named-scoped-explicit-default.md +95 -0
  115. package/skills/vue-debug-guides/reference/slot-render-scope-parent-only.md +135 -0
  116. package/skills/vue-debug-guides/reference/slot-v-slot-on-components-or-templates-only.md +122 -0
  117. package/skills/vue-debug-guides/reference/ssr-hydration-mismatch-causes.md +280 -0
  118. package/skills/vue-debug-guides/reference/ssr-platform-specific-apis.md +256 -0
  119. package/skills/vue-debug-guides/reference/state-ssr-cross-request-pollution.md +276 -0
  120. package/skills/vue-debug-guides/reference/suspense-no-builtin-error-handling.md +127 -0
  121. package/skills/vue-debug-guides/reference/suspense-ssr-hydration-issues.md +159 -0
  122. package/skills/vue-debug-guides/reference/tailwind-dynamic-class-generation.md +144 -0
  123. package/skills/vue-debug-guides/reference/teleport-scoped-styles-limitation.md +191 -0
  124. package/skills/vue-debug-guides/reference/teleport-ssr-hydration.md +152 -0
  125. package/skills/vue-debug-guides/reference/teleport-target-must-exist.md +113 -0
  126. package/skills/vue-debug-guides/reference/template-expressions-restrictions.md +114 -0
  127. package/skills/vue-debug-guides/reference/template-functions-no-side-effects.md +187 -0
  128. package/skills/vue-debug-guides/reference/template-ref-null-with-v-if.md +123 -0
  129. package/skills/vue-debug-guides/reference/template-ref-unwrapping-top-level.md +104 -0
  130. package/skills/vue-debug-guides/reference/template-ref-v-for-order.md +172 -0
  131. package/skills/vue-debug-guides/reference/textarea-no-interpolation.md +72 -0
  132. package/skills/vue-debug-guides/reference/transition-group-flip-inline-elements.md +152 -0
  133. package/skills/vue-debug-guides/reference/transition-group-move-animation-position-absolute.md +130 -0
  134. package/skills/vue-debug-guides/reference/transition-group-no-default-wrapper-vue3.md +152 -0
  135. package/skills/vue-debug-guides/reference/transition-js-hooks-done-callback.md +251 -0
  136. package/skills/vue-debug-guides/reference/transition-nested-duration.md +182 -0
  137. package/skills/vue-debug-guides/reference/transition-reusable-scoped-style.md +245 -0
  138. package/skills/vue-debug-guides/reference/transition-router-view-appear.md +193 -0
  139. package/skills/vue-debug-guides/reference/transition-type-when-mixed.md +172 -0
  140. package/skills/vue-debug-guides/reference/transition-unmount-hook-timing.md +149 -0
  141. package/skills/vue-debug-guides/reference/ts-defineprops-boolean-default-false.md +225 -0
  142. package/skills/vue-debug-guides/reference/ts-defineprops-imported-types-limitations.md +281 -0
  143. package/skills/vue-debug-guides/reference/ts-event-handler-explicit-typing.md +213 -0
  144. package/skills/vue-debug-guides/reference/ts-reactive-no-generic-argument.md +196 -0
  145. package/skills/vue-debug-guides/reference/ts-shallowref-for-dynamic-components.md +218 -0
  146. package/skills/vue-debug-guides/reference/ts-template-ref-null-handling.md +249 -0
  147. package/skills/vue-debug-guides/reference/ts-template-type-casting.md +214 -0
  148. package/skills/vue-debug-guides/reference/ts-withdefaults-mutable-factory-function.md +171 -0
  149. package/skills/vue-debug-guides/reference/undeclared-emits-double-firing.md +195 -0
  150. package/skills/vue-debug-guides/reference/use-template-ref-vue35.md +158 -0
  151. package/skills/vue-debug-guides/reference/v-else-must-follow-v-if.md +136 -0
  152. package/skills/vue-debug-guides/reference/v-for-component-props.md +95 -0
  153. package/skills/vue-debug-guides/reference/v-for-computed-reverse-sort.md +86 -0
  154. package/skills/vue-debug-guides/reference/v-for-key-attribute.md +90 -0
  155. package/skills/vue-debug-guides/reference/v-for-range-starts-at-one.md +66 -0
  156. package/skills/vue-debug-guides/reference/v-if-null-check-order.md +171 -0
  157. package/skills/vue-debug-guides/reference/v-model-ignores-html-attributes.md +83 -0
  158. package/skills/vue-debug-guides/reference/v-model-ime-composition.md +83 -0
  159. package/skills/vue-debug-guides/reference/v-model-number-modifier-behavior.md +124 -0
  160. package/skills/vue-debug-guides/reference/v-show-template-limitation.md +124 -0
  161. package/skills/vue-debug-guides/reference/watch-async-cleanup.md +180 -0
  162. package/skills/vue-debug-guides/reference/watch-async-creation-memory-leak.md +176 -0
  163. package/skills/vue-debug-guides/reference/watch-deep-same-object-reference.md +165 -0
  164. package/skills/vue-debug-guides/reference/watch-flush-timing.md +189 -0
  165. package/skills/vue-debug-guides/reference/watch-reactive-property-getter.md +108 -0
  166. package/skills/vue-debug-guides/reference/watcheffect-async-dependency-tracking.md +173 -0
  167. package/skills/vue-debug-guides/reference/watcheffect-flush-post-for-refs.md +176 -0
  168. package/skills/vue-jsx-best-practices/SKILL.md +12 -0
  169. package/skills/vue-jsx-best-practices/reference/render-function-jsx-vue-vs-react.md +141 -0
  170. package/skills/vue-options-api-best-practices/SKILL.md +23 -0
  171. package/skills/vue-options-api-best-practices/reference/no-arrow-functions-in-lifecycle-hooks.md +95 -0
  172. package/skills/vue-options-api-best-practices/reference/no-arrow-functions-in-methods.md +68 -0
  173. package/skills/vue-options-api-best-practices/reference/stateful-methods-lifecycle.md +61 -0
  174. package/skills/vue-options-api-best-practices/reference/ts-options-api-arrow-functions-validators.md +141 -0
  175. package/skills/vue-options-api-best-practices/reference/ts-options-api-computed-return-types.md +192 -0
  176. package/skills/vue-options-api-best-practices/reference/ts-options-api-proptype-complex-types.md +212 -0
  177. package/skills/vue-options-api-best-practices/reference/ts-options-api-provide-inject-limitations.md +135 -0
  178. package/skills/vue-options-api-best-practices/reference/ts-options-api-type-event-handlers.md +202 -0
  179. package/skills/vue-options-api-best-practices/reference/ts-options-api-use-definecomponent.md +172 -0
  180. package/skills/vue-options-api-best-practices/reference/ts-strict-mode-options-api.md +197 -0
  181. package/skills/vue-pinia-best-practices/SKILL.md +21 -0
  182. package/skills/vue-pinia-best-practices/reference/pinia-no-active-pinia-error.md +248 -0
  183. package/skills/vue-pinia-best-practices/reference/pinia-setup-store-return-all-state.md +227 -0
  184. package/skills/vue-pinia-best-practices/reference/pinia-store-destructuring-breaks-reactivity.md +193 -0
  185. package/skills/vue-pinia-best-practices/reference/state-url-for-ephemeral-filters.md +238 -0
  186. package/skills/vue-pinia-best-practices/reference/state-use-pinia-for-large-apps.md +262 -0
  187. package/skills/vue-pinia-best-practices/reference/store-method-binding-parentheses.md +191 -0
  188. package/skills/vue-router-best-practices/SKILL.md +23 -0
  189. package/skills/vue-router-best-practices/reference/router-beforeenter-no-param-trigger.md +167 -0
  190. package/skills/vue-router-best-practices/reference/router-beforerouteenter-no-this.md +176 -0
  191. package/skills/vue-router-best-practices/reference/router-guard-async-await-pattern.md +227 -0
  192. package/skills/vue-router-best-practices/reference/router-navigation-guard-infinite-loop.md +187 -0
  193. package/skills/vue-router-best-practices/reference/router-navigation-guard-next-deprecated.md +150 -0
  194. package/skills/vue-router-best-practices/reference/router-param-change-no-lifecycle.md +181 -0
  195. package/skills/vue-router-best-practices/reference/router-simple-routing-cleanup.md +209 -0
  196. package/skills/vue-router-best-practices/reference/router-use-vue-router-for-production.md +183 -0
  197. package/skills/vue-testing-best-practices/SKILL.md +29 -0
  198. package/skills/vue-testing-best-practices/reference/async-component-testing.md +163 -0
  199. package/skills/vue-testing-best-practices/reference/teleport-testing-complexity.md +158 -0
  200. package/skills/vue-testing-best-practices/reference/testing-async-await-flushpromises.md +175 -0
  201. package/skills/vue-testing-best-practices/reference/testing-browser-vs-node-runners.md +208 -0
  202. package/skills/vue-testing-best-practices/reference/testing-component-blackbox-approach.md +144 -0
  203. package/skills/vue-testing-best-practices/reference/testing-composables-helper-wrapper.md +238 -0
  204. package/skills/vue-testing-best-practices/reference/testing-e2e-playwright-recommended.md +242 -0
  205. package/skills/vue-testing-best-practices/reference/testing-no-snapshot-only.md +197 -0
  206. package/skills/vue-testing-best-practices/reference/testing-pinia-store-setup.md +228 -0
  207. package/skills/vue-testing-best-practices/reference/testing-suspense-async-components.md +229 -0
  208. package/skills/vue-testing-best-practices/reference/testing-vitest-recommended-for-vue.md +204 -0
@@ -0,0 +1,276 @@
1
+ ---
2
+ title: Prevent Cross-Request State Pollution in SSR Applications
3
+ impact: CRITICAL
4
+ impactDescription: Singleton stores in SSR share state across all server requests, potentially leaking user data between requests
5
+ type: gotcha
6
+ tags: [vue3, ssr, state-management, pinia, vuex, security, server-side-rendering, nuxt]
7
+ ---
8
+
9
+ # Prevent Cross-Request State Pollution in SSR Applications
10
+
11
+ **Impact: CRITICAL** - In Server-Side Rendering (SSR) applications, a singleton store pattern creates a single instance that is shared across all server requests. This means data from one user's request could leak into another user's response, causing serious security and data integrity issues.
12
+
13
+ This is one of the most critical gotchas in Vue state management that can have severe production consequences.
14
+
15
+ ## Task Checklist
16
+
17
+ - [ ] Never use a singleton store pattern in SSR applications
18
+ - [ ] Create a fresh store instance per request when using SSR
19
+ - [ ] Use Pinia which handles SSR state management correctly
20
+ - [ ] Test SSR state isolation with concurrent requests
21
+ - [ ] Review any global reactive state for SSR compatibility
22
+
23
+ ## The Problem: Singleton State in SSR
24
+
25
+ ```javascript
26
+ // store.js - DANGEROUS for SSR
27
+ import { reactive } from 'vue'
28
+
29
+ // This is a singleton - same instance for ALL requests
30
+ export const store = reactive({
31
+ user: null,
32
+ cart: [],
33
+ preferences: {}
34
+ })
35
+ ```
36
+
37
+ **What happens in SSR:**
38
+
39
+ 1. Request A comes in for User A
40
+ 2. Server sets `store.user = userA`
41
+ 3. Before response completes, Request B arrives for User B
42
+ 4. Request B sees `store.user = userA` (User A's data leaked!)
43
+ 5. Server sets `store.user = userB`
44
+ 6. Request A's response might now contain User B's data
45
+
46
+ This creates unpredictable behavior and potential security vulnerabilities.
47
+
48
+ ## Solution 1: Use Pinia (Recommended)
49
+
50
+ Pinia handles SSR correctly by creating fresh store instances per request:
51
+
52
+ ```javascript
53
+ // stores/user.js
54
+ import { defineStore } from 'pinia'
55
+
56
+ export const useUserStore = defineStore('user', {
57
+ state: () => ({
58
+ user: null,
59
+ preferences: {}
60
+ }),
61
+ actions: {
62
+ setUser(user) {
63
+ this.user = user
64
+ }
65
+ }
66
+ })
67
+ ```
68
+
69
+ ```javascript
70
+ // main.js (or entry-server.js)
71
+ import { createPinia } from 'pinia'
72
+ import { createApp } from 'vue'
73
+ import App from './App.vue'
74
+
75
+ // For SSR: Create fresh instances per request
76
+ export function createAppInstance() {
77
+ const app = createApp(App)
78
+ const pinia = createPinia()
79
+
80
+ app.use(pinia)
81
+
82
+ return { app, pinia }
83
+ }
84
+ ```
85
+
86
+ ```javascript
87
+ // entry-server.js
88
+ import { createAppInstance } from './main'
89
+ import { renderToString } from 'vue/server-renderer'
90
+
91
+ export async function render(url, context) {
92
+ // Fresh app and store instance per request
93
+ const { app, pinia } = createAppInstance()
94
+
95
+ // ... setup router, fetch data, etc.
96
+
97
+ const html = await renderToString(app)
98
+
99
+ // Serialize state for client hydration
100
+ const state = pinia.state.value
101
+
102
+ return { html, state }
103
+ }
104
+ ```
105
+
106
+ ```javascript
107
+ // entry-client.js - Hydrate from serialized state
108
+ import { createAppInstance } from './main'
109
+
110
+ const { app, pinia } = createAppInstance()
111
+
112
+ // Restore server state before mounting
113
+ if (window.__PINIA_STATE__) {
114
+ pinia.state.value = window.__PINIA_STATE__
115
+ }
116
+
117
+ app.mount('#app')
118
+ ```
119
+
120
+ ## Solution 2: Factory Pattern for Hand-Rolled State
121
+
122
+ If not using Pinia, create a factory function:
123
+
124
+ ```javascript
125
+ // store.js - SSR-safe with factory
126
+ import { reactive, readonly } from 'vue'
127
+
128
+ // Factory function creates fresh state per call
129
+ export function createStore() {
130
+ const state = reactive({
131
+ user: null,
132
+ cart: [],
133
+ preferences: {}
134
+ })
135
+
136
+ return {
137
+ state: readonly(state),
138
+ setUser(user) {
139
+ state.user = user
140
+ },
141
+ addToCart(item) {
142
+ state.cart.push(item)
143
+ }
144
+ }
145
+ }
146
+ ```
147
+
148
+ ```javascript
149
+ // entry-server.js
150
+ import { createStore } from './store'
151
+ import { provide } from 'vue'
152
+
153
+ export async function render(url) {
154
+ const app = createApp(App)
155
+
156
+ // Fresh store instance for this request only
157
+ const store = createStore()
158
+ app.provide('store', store)
159
+
160
+ // ... render
161
+ }
162
+ ```
163
+
164
+ ## Solution 3: Context-Based State (Advanced)
165
+
166
+ For frameworks like Nuxt, use request context:
167
+
168
+ ```javascript
169
+ // composables/useRequestState.js
170
+ import { useSSRContext } from 'vue'
171
+
172
+ export function useRequestState(key, initialValue) {
173
+ if (import.meta.env.SSR) {
174
+ const ctx = useSSRContext()
175
+ ctx.state = ctx.state || {}
176
+
177
+ if (!(key in ctx.state)) {
178
+ ctx.state[key] = initialValue()
179
+ }
180
+
181
+ return ctx.state[key]
182
+ }
183
+
184
+ // Client-side: use regular reactive state
185
+ return reactive(initialValue())
186
+ }
187
+ ```
188
+
189
+ ## Nuxt.js Handles This Automatically
190
+
191
+ In Nuxt 3, state isolation is handled automatically:
192
+
193
+ ```javascript
194
+ // Nuxt automatically creates fresh Pinia instance per request
195
+ // You can use stores normally
196
+
197
+ export default defineNuxtPlugin(async (nuxtApp) => {
198
+ const userStore = useUserStore()
199
+ await userStore.fetchUser()
200
+ })
201
+ ```
202
+
203
+ ## Testing for State Pollution
204
+
205
+ ```javascript
206
+ // test/ssr-state-isolation.test.js
207
+ import { describe, it, expect } from 'vitest'
208
+ import { render } from './entry-server'
209
+
210
+ describe('SSR State Isolation', () => {
211
+ it('should not leak state between concurrent requests', async () => {
212
+ // Simulate concurrent requests
213
+ const [result1, result2] = await Promise.all([
214
+ render('/user/1', { userId: '1' }),
215
+ render('/user/2', { userId: '2' })
216
+ ])
217
+
218
+ // Each should have their own user data
219
+ expect(result1.html).toContain('User 1')
220
+ expect(result2.html).toContain('User 2')
221
+
222
+ // State should not be mixed
223
+ expect(result1.html).not.toContain('User 2')
224
+ expect(result2.html).not.toContain('User 1')
225
+ })
226
+ })
227
+ ```
228
+
229
+ ```javascript
230
+ // Alternative: Test store isolation directly
231
+ import { createApp } from './app.js'
232
+
233
+ test('requests do not share state', async () => {
234
+ // Simulate two concurrent requests
235
+ const { app: app1, store: store1 } = createApp()
236
+ const { app: app2, store: store2 } = createApp()
237
+
238
+ store1.user = { id: 1, name: 'Alice' }
239
+ store2.user = { id: 2, name: 'Bob' }
240
+
241
+ // Each should have its own state
242
+ expect(store1.user.name).toBe('Alice')
243
+ expect(store2.user.name).toBe('Bob')
244
+ })
245
+ ```
246
+
247
+ ## Red Flags to Watch For
248
+
249
+ ```javascript
250
+ // ANY module-level reactive state is dangerous in SSR
251
+
252
+ // BAD: Module-level reactive
253
+ export const globalUser = ref(null)
254
+
255
+ // BAD: Module-level reactive object
256
+ export const appState = reactive({})
257
+
258
+ // BAD: Shared Map/Set
259
+ export const cache = new Map()
260
+
261
+ // BAD: Even plain objects can be problematic
262
+ let requestCount = 0 // Shared across requests
263
+ ```
264
+
265
+ ## Why Pinia is Recommended for SSR
266
+
267
+ 1. **Automatic request isolation** - Fresh store instances per request
268
+ 2. **Built-in state serialization** - Easy hydration on client
269
+ 3. **DevTools support** - Debug state on both server and client
270
+ 4. **TypeScript support** - Type-safe state management
271
+ 5. **Tested patterns** - Battle-tested SSR handling
272
+
273
+ ## Reference
274
+ - [Vue.js State Management - SSR Considerations](https://vuejs.org/guide/scaling-up/state-management.html#ssr-considerations)
275
+ - [Pinia SSR Guide](https://pinia.vuejs.org/ssr/)
276
+ - [Vue SSR Guide](https://vuejs.org/guide/scaling-up/ssr.html)
@@ -0,0 +1,127 @@
1
+ # Suspense Has No Built-in Error Handling
2
+
3
+ ## Rule
4
+
5
+ `<Suspense>` does not provide error handling via the component itself. You must implement error handling using `errorCaptured` option or `onErrorCaptured()` hook in a parent component to catch async errors.
6
+
7
+ ## Why This Matters
8
+
9
+ Without explicit error handling, async errors in suspended components will propagate uncaught, potentially crashing the application or leaving users stuck on loading states. Unlike React's Error Boundaries, Vue's Suspense requires manual error boundary implementation.
10
+
11
+ ## Bad Code
12
+
13
+ ```vue
14
+ <script setup>
15
+ // No error handling - async errors will propagate uncaught
16
+ </script>
17
+
18
+ <template>
19
+ <Suspense>
20
+ <AsyncComponent />
21
+ <template #fallback>
22
+ Loading...
23
+ </template>
24
+ </Suspense>
25
+ </template>
26
+ ```
27
+
28
+ ## Good Code
29
+
30
+ ```vue
31
+ <script setup>
32
+ import { ref, onErrorCaptured } from 'vue'
33
+ import AsyncComponent from './AsyncComponent.vue'
34
+
35
+ const error = ref(null)
36
+
37
+ onErrorCaptured((err) => {
38
+ error.value = err
39
+ return false // Prevent error from propagating further
40
+ })
41
+ </script>
42
+
43
+ <template>
44
+ <div v-if="error" class="error-state">
45
+ <p>Something went wrong: {{ error.message }}</p>
46
+ <button @click="error = null">Retry</button>
47
+ </div>
48
+
49
+ <Suspense v-else>
50
+ <AsyncComponent />
51
+ <template #fallback>
52
+ Loading...
53
+ </template>
54
+ </Suspense>
55
+ </template>
56
+ ```
57
+
58
+ ## Reusable Error Boundary Pattern
59
+
60
+ ```vue
61
+ <!-- ErrorBoundary.vue -->
62
+ <script setup>
63
+ import { ref, onErrorCaptured } from 'vue'
64
+
65
+ const props = defineProps({
66
+ fallback: {
67
+ type: String,
68
+ default: 'Something went wrong'
69
+ }
70
+ })
71
+
72
+ const emit = defineEmits(['error'])
73
+
74
+ const error = ref(null)
75
+
76
+ onErrorCaptured((err, instance, info) => {
77
+ error.value = { err, instance, info }
78
+ emit('error', { err, instance, info })
79
+ return false
80
+ })
81
+
82
+ const reset = () => {
83
+ error.value = null
84
+ }
85
+
86
+ defineExpose({ reset })
87
+ </script>
88
+
89
+ <template>
90
+ <slot v-if="!error" />
91
+ <slot v-else name="error" :error="error" :reset="reset">
92
+ <div class="error-boundary">
93
+ {{ fallback }}
94
+ <button @click="reset">Retry</button>
95
+ </div>
96
+ </slot>
97
+ </template>
98
+ ```
99
+
100
+ ```vue
101
+ <!-- Usage -->
102
+ <template>
103
+ <ErrorBoundary @error="logError">
104
+ <Suspense>
105
+ <AsyncDashboard />
106
+ <template #fallback>Loading dashboard...</template>
107
+ </Suspense>
108
+
109
+ <template #error="{ error, reset }">
110
+ <DashboardError :error="error" @retry="reset" />
111
+ </template>
112
+ </ErrorBoundary>
113
+ </template>
114
+ ```
115
+
116
+ ## Key Points
117
+
118
+ 1. Always wrap `<Suspense>` with error handling logic in production
119
+ 2. Use `onErrorCaptured` for Composition API or `errorCaptured` option for Options API
120
+ 3. Return `false` from the error handler to stop propagation
121
+ 4. Consider creating a reusable `ErrorBoundary` component to reduce boilerplate
122
+ 5. Provide a way for users to retry failed operations
123
+
124
+ ## References
125
+
126
+ - [Vue.js Suspense Documentation](https://vuejs.org/guide/built-ins/suspense#error-handling)
127
+ - [Vue.js onErrorCaptured](https://vuejs.org/api/composition-api-lifecycle#onerrorcaptured)
@@ -0,0 +1,159 @@
1
+ # Suspense SSR Hydration Issues and Workarounds
2
+
3
+ ## Rule
4
+
5
+ `<Suspense>` has known issues with SSR hydration, particularly with async components. During initial hydration, Suspense may not properly include child components within its "cloak of suspense," leading to hydration mismatches, flickering, or runtime crashes.
6
+
7
+ ## Why This Matters
8
+
9
+ In SSR applications, hydration mismatches cause:
10
+ - Visual flickering as the client re-renders
11
+ - Loss of state in affected components
12
+ - Console warnings in development (silent failures in production)
13
+ - Potential runtime crashes in edge cases
14
+ - Poor user experience, especially on slower networks
15
+
16
+ ## Bad Code
17
+
18
+ ```vue
19
+ <template>
20
+ <!-- Async component directly in Suspense can fail hydration -->
21
+ <Suspense>
22
+ <AsyncDashboard />
23
+ <template #fallback>
24
+ Loading...
25
+ </template>
26
+ </Suspense>
27
+ </template>
28
+
29
+ <script setup>
30
+ import { defineAsyncComponent } from 'vue'
31
+
32
+ const AsyncDashboard = defineAsyncComponent(
33
+ () => import('./Dashboard.vue')
34
+ )
35
+ </script>
36
+ ```
37
+
38
+ ## Good Code
39
+
40
+ ### Solution 1: Wrap Async Components with Suspense
41
+
42
+ ```vue
43
+ <template>
44
+ <!-- Each async component wrapped in its own Suspense -->
45
+ <div class="dashboard">
46
+ <Suspense>
47
+ <AsyncHeader />
48
+ <template #fallback><HeaderSkeleton /></template>
49
+ </Suspense>
50
+
51
+ <Suspense>
52
+ <AsyncContent />
53
+ <template #fallback><ContentSkeleton /></template>
54
+ </Suspense>
55
+ </div>
56
+ </template>
57
+ ```
58
+
59
+ ### Solution 2: Use ClientOnly Wrapper (Nuxt/SSR Frameworks)
60
+
61
+ ```vue
62
+ <template>
63
+ <!-- Prevent SSR for problematic async components -->
64
+ <ClientOnly>
65
+ <Suspense>
66
+ <AsyncDashboard />
67
+ <template #fallback>
68
+ Loading dashboard...
69
+ </template>
70
+ </Suspense>
71
+
72
+ <template #fallback>
73
+ <DashboardSkeleton />
74
+ </template>
75
+ </ClientOnly>
76
+ </template>
77
+ ```
78
+
79
+ ### Solution 3: Prefetch with Proper Stale Time (with TanStack Query)
80
+
81
+ ```vue
82
+ <script setup>
83
+ import { useQuery, useQueryClient } from '@tanstack/vue-query'
84
+
85
+ // IMPORTANT: All useQuery calls must be BEFORE any await
86
+ const { data, suspense } = useQuery({
87
+ queryKey: ['dashboard'],
88
+ queryFn: fetchDashboardData,
89
+ staleTime: 1000 * 60 * 5, // 5 minutes - prevents refetch after hydration
90
+ })
91
+
92
+ // Wait for suspense AFTER all useQuery calls
93
+ await suspense()
94
+
95
+ // Now safe to use data
96
+ </script>
97
+ ```
98
+
99
+ ### Solution 4: Handle Hydration Errors Gracefully
100
+
101
+ ```vue
102
+ <script setup>
103
+ import { ref, onErrorCaptured, onMounted } from 'vue'
104
+
105
+ const hydrationError = ref(false)
106
+ const isClient = ref(false)
107
+
108
+ onMounted(() => {
109
+ isClient.value = true
110
+ })
111
+
112
+ onErrorCaptured((err) => {
113
+ if (err.message?.includes('hydration')) {
114
+ hydrationError.value = true
115
+ return false
116
+ }
117
+ })
118
+ </script>
119
+
120
+ <template>
121
+ <div v-if="hydrationError" class="hydration-recovery">
122
+ <!-- Force client-only re-render -->
123
+ <Suspense v-if="isClient">
124
+ <AsyncContent />
125
+ <template #fallback>Recovering...</template>
126
+ </Suspense>
127
+ </div>
128
+
129
+ <Suspense v-else>
130
+ <AsyncContent />
131
+ <template #fallback>Loading...</template>
132
+ </Suspense>
133
+ </template>
134
+ ```
135
+
136
+ ## Common SSR + Suspense Issues
137
+
138
+ | Issue | Cause | Solution |
139
+ |-------|-------|----------|
140
+ | Hydration mismatch | Async chunk not loaded in time | Wrap with Suspense or use ClientOnly |
141
+ | Empty flash on Safari | Slow chunk loading | Preload critical chunks, use skeleton |
142
+ | useQuery after await error | Vue context lost after await | Put all useQuery calls before any await |
143
+ | Immediate refetch after hydration | staleTime too low | Set appropriate staleTime value |
144
+
145
+ ## Key Points
146
+
147
+ 1. Suspense + SSR has known edge cases - test thoroughly
148
+ 2. Safari has slower chunk loading that triggers more hydration issues
149
+ 3. With data-fetching libraries, ensure queries are set up before awaiting suspense
150
+ 4. Consider ClientOnly wrappers for non-critical async content
151
+ 5. Set appropriate staleTime to prevent unnecessary refetches after hydration
152
+ 6. Use skeleton screens that match server-rendered content structure
153
+
154
+ ## References
155
+
156
+ - [Vue.js Suspense Documentation](https://vuejs.org/guide/built-ins/suspense)
157
+ - [Vue Issue #6638 - Suspense hydration](https://github.com/vuejs/core/issues/6638)
158
+ - [Vue Issue #7672 - defineAsyncComponent SSR](https://github.com/vuejs/core/issues/7672)
159
+ - [TanStack Query SSR Discussion](https://github.com/TanStack/query/discussions/4870)
@@ -0,0 +1,144 @@
1
+ # Tailwind CSS Dynamic Class Generation
2
+
3
+ ## Rule
4
+
5
+ Never construct Tailwind CSS class names dynamically using string concatenation or template literals. Tailwind's build process cannot detect dynamically generated class names, causing styles to be missing in production.
6
+
7
+ ## Why This Matters
8
+
9
+ - Tailwind uses static analysis at build time to determine which CSS classes to include
10
+ - Dynamically constructed class names (e.g., `bg-${color}-500`) are invisible to Tailwind's scanner
11
+ - Classes work in development with JIT but fail silently in production builds
12
+ - This is a common source of "it works locally but not in production" bugs
13
+
14
+ ## Bad Code
15
+
16
+ ```vue
17
+ <script setup>
18
+ const props = defineProps({
19
+ color: String, // 'red', 'blue', 'green'
20
+ size: String // 'sm', 'md', 'lg'
21
+ })
22
+ </script>
23
+
24
+ <template>
25
+ <!-- WRONG: Tailwind cannot detect these classes -->
26
+ <div :class="`bg-${color}-500 text-${size}`">
27
+ Content
28
+ </div>
29
+
30
+ <!-- WRONG: String concatenation -->
31
+ <div :class="'p-' + padding">
32
+ Content
33
+ </div>
34
+
35
+ <!-- WRONG: Template literal in array -->
36
+ <div :class="[`gap-x-${spacing}`]">
37
+ Content
38
+ </div>
39
+ </template>
40
+ ```
41
+
42
+ ## Good Code
43
+
44
+ ```vue
45
+ <script setup>
46
+ const props = defineProps({
47
+ color: String,
48
+ size: String
49
+ })
50
+
51
+ // Use a mapping object with complete class names
52
+ const colorClasses = {
53
+ red: 'bg-red-500',
54
+ blue: 'bg-blue-500',
55
+ green: 'bg-green-500'
56
+ }
57
+
58
+ const sizeClasses = {
59
+ sm: 'text-sm p-2',
60
+ md: 'text-base p-4',
61
+ lg: 'text-lg p-6'
62
+ }
63
+ </script>
64
+
65
+ <template>
66
+ <!-- CORRECT: Full class names that Tailwind can detect -->
67
+ <div :class="[colorClasses[color], sizeClasses[size]]">
68
+ Content
69
+ </div>
70
+ </template>
71
+ ```
72
+
73
+ ## Using Conditional Objects
74
+
75
+ ```vue
76
+ <script setup>
77
+ const props = defineProps({
78
+ variant: String // 'primary', 'secondary', 'danger'
79
+ })
80
+ </script>
81
+
82
+ <template>
83
+ <!-- CORRECT: All class names are complete strings -->
84
+ <button :class="{
85
+ 'bg-blue-500 hover:bg-blue-600': variant === 'primary',
86
+ 'bg-gray-500 hover:bg-gray-600': variant === 'secondary',
87
+ 'bg-red-500 hover:bg-red-600': variant === 'danger'
88
+ }">
89
+ Click me
90
+ </button>
91
+ </template>
92
+ ```
93
+
94
+ ## Safelist for Truly Dynamic Classes
95
+
96
+ If you must use dynamic classes, add them to Tailwind's safelist:
97
+
98
+ ```javascript
99
+ // tailwind.config.js
100
+ module.exports = {
101
+ safelist: [
102
+ 'bg-red-500',
103
+ 'bg-blue-500',
104
+ 'bg-green-500',
105
+ // Or use patterns (use sparingly - increases bundle size)
106
+ {
107
+ pattern: /bg-(red|blue|green)-(100|500|900)/
108
+ }
109
+ ]
110
+ }
111
+ ```
112
+
113
+ ## Alternative: CSS Custom Properties
114
+
115
+ For truly dynamic values, use CSS custom properties:
116
+
117
+ ```vue
118
+ <script setup>
119
+ const props = defineProps({
120
+ customColor: String // Any hex color
121
+ })
122
+ </script>
123
+
124
+ <template>
125
+ <!-- Use CSS variable for truly dynamic values -->
126
+ <div
127
+ class="dynamic-bg"
128
+ :style="{ '--dynamic-color': customColor }"
129
+ >
130
+ Content
131
+ </div>
132
+ </template>
133
+
134
+ <style>
135
+ .dynamic-bg {
136
+ background-color: var(--dynamic-color);
137
+ }
138
+ </style>
139
+ ```
140
+
141
+ ## References
142
+
143
+ - [Tailwind CSS Dynamic Class Names](https://tailwindcss.com/docs/content-configuration#dynamic-class-names)
144
+ - [Tailwind Safelist](https://tailwindcss.com/docs/content-configuration#safelisting-classes)