@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,122 @@
1
+ ---
2
+ title: v-slot Can Only Be Used on Components or Template Tags
3
+ impact: HIGH
4
+ impactDescription: Using v-slot on HTML elements causes compilation errors
5
+ type: gotcha
6
+ tags: [vue3, slots, v-slot, compilation-error, common-mistake]
7
+ ---
8
+
9
+ # v-slot Can Only Be Used on Components or Template Tags
10
+
11
+ **Impact: HIGH** - The `v-slot` directive (and its shorthand `#`) can only be used on Vue components or `<template>` tags. Using it on native HTML elements like `<div>` or `<span>` causes a Vue compilation error.
12
+
13
+ ## Task Checklist
14
+
15
+ - [ ] Only use `v-slot` on component elements or `<template>` tags
16
+ - [ ] When using default scoped slot shorthand, apply to the component itself
17
+ - [ ] For named slots, always use `<template #name>` syntax
18
+
19
+ **Incorrect:**
20
+ ```vue
21
+ <template>
22
+ <!-- BAD: v-slot on a native HTML element -->
23
+ <div v-slot:header>
24
+ <h1>Title</h1>
25
+ </div>
26
+
27
+ <!-- BAD: Shorthand on HTML element -->
28
+ <span #default="{ item }">
29
+ {{ item.name }}
30
+ </span>
31
+
32
+ <!-- BAD: v-slot inside a plain HTML element -->
33
+ <div>
34
+ <p v-slot:content>Some text</p>
35
+ </div>
36
+ </template>
37
+ ```
38
+
39
+ These cause the error: `v-slot can only be used on components or <template> tags`
40
+
41
+ **Correct:**
42
+ ```vue
43
+ <template>
44
+ <!-- GOOD: v-slot on component element (default scoped slot) -->
45
+ <MyComponent v-slot="{ item }">
46
+ {{ item.name }}
47
+ </MyComponent>
48
+
49
+ <!-- GOOD: Named slots use template tags -->
50
+ <BaseLayout>
51
+ <template #header>
52
+ <h1>Title</h1>
53
+ </template>
54
+
55
+ <template #default>
56
+ <p>Main content</p>
57
+ </template>
58
+
59
+ <template #footer>
60
+ <p>Footer content</p>
61
+ </template>
62
+ </BaseLayout>
63
+
64
+ <!-- GOOD: Shorthand on component for default slot -->
65
+ <FancyList #default="{ item }">
66
+ <div>{{ item.name }}</div>
67
+ </FancyList>
68
+ </template>
69
+ ```
70
+
71
+ ## Common Scenarios
72
+
73
+ ### Wrapping Slot Content in HTML
74
+ If you need HTML wrappers around slot content, put them inside the template:
75
+
76
+ ```vue
77
+ <!-- WRONG -->
78
+ <MyComponent>
79
+ <div v-slot:header class="header-wrapper">
80
+ <h1>Title</h1>
81
+ </div>
82
+ </MyComponent>
83
+
84
+ <!-- CORRECT -->
85
+ <MyComponent>
86
+ <template #header>
87
+ <div class="header-wrapper">
88
+ <h1>Title</h1>
89
+ </div>
90
+ </template>
91
+ </MyComponent>
92
+ ```
93
+
94
+ ### Multiple v-slot on Same Element
95
+ Another error occurs when you have multiple v-slot directives - only the first is recognized:
96
+
97
+ ```vue
98
+ <!-- BAD: Multiple v-slot directives -->
99
+ <MyComponent v-slot:header v-slot:footer>
100
+ Content
101
+ </MyComponent>
102
+
103
+ <!-- GOOD: Separate template for each slot -->
104
+ <MyComponent>
105
+ <template #header>Header</template>
106
+ <template #footer>Footer</template>
107
+ </MyComponent>
108
+ ```
109
+
110
+ ## Valid v-slot Locations
111
+
112
+ | Element Type | v-slot Allowed? | Example |
113
+ |--------------|-----------------|---------|
114
+ | Component | Yes | `<MyComponent v-slot="props">` |
115
+ | `<template>` | Yes | `<template #header>` |
116
+ | `<div>` | No | Compilation error |
117
+ | `<span>` | No | Compilation error |
118
+ | Any HTML element | No | Compilation error |
119
+
120
+ ## Reference
121
+ - [Vue.js Slots](https://vuejs.org/guide/components/slots.html)
122
+ - [DeepScan - vue-misused-v-slot](https://deepscan.io/docs/rules/vue-misused-v-slot)
@@ -0,0 +1,280 @@
1
+ ---
2
+ title: Understand and Fix SSR Hydration Mismatches
3
+ impact: HIGH
4
+ impactDescription: Hydration mismatches cause visual flickering, performance loss, and broken functionality
5
+ type: gotcha
6
+ tags: [vue3, ssr, hydration, debugging, nuxt, server-side-rendering]
7
+ ---
8
+
9
+ # Understand and Fix SSR Hydration Mismatches
10
+
11
+ **Impact: HIGH** - Hydration mismatches occur when the HTML rendered on the client differs from what the server rendered. Vue attempts to recover by discarding and re-rendering mismatched nodes, causing performance degradation, visual flickering, and potentially broken event handlers.
12
+
13
+ Understanding the common causes helps you prevent and debug these issues effectively.
14
+
15
+ ## Task Checklist
16
+
17
+ - [ ] Validate HTML structure for proper nesting (no div in p, no nested a tags)
18
+ - [ ] Move random value generation to onMounted or use seeded randoms
19
+ - [ ] Format dates/times on client side only
20
+ - [ ] Use `data-allow-mismatch` (Vue 3.5+) for intentional mismatches
21
+ - [ ] Check for browser-modified HTML in dev tools
22
+
23
+ ## Cause 1: Invalid HTML Nesting
24
+
25
+ Browsers auto-correct invalid HTML, creating different DOM than Vue expects.
26
+
27
+ **Incorrect:**
28
+ ```vue
29
+ <template>
30
+ <!-- WRONG: <div> cannot be inside <p> -->
31
+ <p>
32
+ <div>This will break hydration</div>
33
+ </p>
34
+
35
+ <!-- WRONG: <a> cannot be inside <a> -->
36
+ <a href="/parent">
37
+ <a href="/child">Nested link</a>
38
+ </a>
39
+
40
+ <!-- WRONG: Block elements in inline elements -->
41
+ <span>
42
+ <div>Block in inline</div>
43
+ </span>
44
+ </template>
45
+ ```
46
+
47
+ Browser converts the first example to:
48
+ ```html
49
+ <p></p>
50
+ <div>This will break hydration</div>
51
+ <p></p>
52
+ ```
53
+
54
+ **Correct:**
55
+ ```vue
56
+ <template>
57
+ <!-- CORRECT: Use appropriate nesting -->
58
+ <div>
59
+ <div>This works fine</div>
60
+ </div>
61
+
62
+ <!-- CORRECT: Single link with event handling -->
63
+ <a href="/parent" @click="handleParentClick">
64
+ <span @click.stop="handleChildClick">Nested action</span>
65
+ </a>
66
+
67
+ <!-- CORRECT: Block element wrapper -->
68
+ <div>
69
+ <div>Block in block</div>
70
+ </div>
71
+ </template>
72
+ ```
73
+
74
+ ## Cause 2: Random Values in Render
75
+
76
+ Server and client generate different random values.
77
+
78
+ **Incorrect:**
79
+ ```vue
80
+ <template>
81
+ <!-- WRONG: Different ID on server vs client -->
82
+ <div :id="'field-' + Math.random()">
83
+ Form field
84
+ </div>
85
+
86
+ <!-- WRONG: Random order differs -->
87
+ <div v-for="item in shuffledItems" :key="item.id">
88
+ {{ item.name }}
89
+ </div>
90
+ </template>
91
+
92
+ <script setup>
93
+ import { computed } from 'vue'
94
+
95
+ const items = [/* ... */]
96
+
97
+ // WRONG: Random shuffle runs differently on server and client
98
+ const shuffledItems = computed(() =>
99
+ [...items].sort(() => Math.random() - 0.5)
100
+ )
101
+ </script>
102
+ ```
103
+
104
+ **Correct - Client-Only Random:**
105
+ ```vue
106
+ <template>
107
+ <div :id="fieldId">
108
+ Form field
109
+ </div>
110
+
111
+ <div v-for="item in displayItems" :key="item.id">
112
+ {{ item.name }}
113
+ </div>
114
+ </template>
115
+
116
+ <script setup>
117
+ import { ref, onMounted } from 'vue'
118
+
119
+ const items = [/* ... */]
120
+
121
+ // CORRECT: Start with deterministic value
122
+ const fieldId = ref('field-default')
123
+ const displayItems = ref(items) // Original order on server
124
+
125
+ onMounted(() => {
126
+ // Randomize only on client
127
+ fieldId.value = 'field-' + Math.random().toString(36).slice(2)
128
+ displayItems.value = [...items].sort(() => Math.random() - 0.5)
129
+ })
130
+ </script>
131
+ ```
132
+
133
+ **Correct - Seeded Random:**
134
+ ```javascript
135
+ // utils/seededRandom.js
136
+ export function createSeededRandom(seed) {
137
+ return function() {
138
+ seed = (seed * 9301 + 49297) % 233280
139
+ return seed / 233280
140
+ }
141
+ }
142
+
143
+ // Use same seed on server and client
144
+ const seed = 12345 // Could be based on user ID, page, etc.
145
+ const random = createSeededRandom(seed)
146
+ ```
147
+
148
+ ## Cause 3: Timezone and Date Differences
149
+
150
+ Server may be in different timezone than client.
151
+
152
+ **Incorrect:**
153
+ ```vue
154
+ <template>
155
+ <!-- WRONG: Server time != client time -->
156
+ <span>{{ new Date().toLocaleTimeString() }}</span>
157
+
158
+ <!-- WRONG: Server formats dates in server's timezone -->
159
+ <span>{{ formatDate(article.createdAt) }}</span>
160
+ </template>
161
+
162
+ <script setup>
163
+ function formatDate(date) {
164
+ return new Date(date).toLocaleDateString()
165
+ }
166
+ </script>
167
+ ```
168
+
169
+ **Correct:**
170
+ ```vue
171
+ <template>
172
+ <!-- CORRECT: Render placeholder, update on client -->
173
+ <span>{{ displayTime || 'Loading...' }}</span>
174
+
175
+ <!-- CORRECT: Use UTC or defer to client -->
176
+ <span>{{ formattedDate }}</span>
177
+ </template>
178
+
179
+ <script setup>
180
+ import { ref, computed, onMounted } from 'vue'
181
+
182
+ const props = defineProps(['article'])
183
+ const displayTime = ref(null)
184
+ const isClient = ref(false)
185
+
186
+ onMounted(() => {
187
+ displayTime.value = new Date().toLocaleTimeString()
188
+ isClient.value = true
189
+ })
190
+
191
+ // CORRECT: Server renders UTC, client converts to local
192
+ const formattedDate = computed(() => {
193
+ if (!props.article?.createdAt) return ''
194
+
195
+ if (isClient.value) {
196
+ // Client: user's local timezone
197
+ return new Date(props.article.createdAt).toLocaleDateString()
198
+ } else {
199
+ // Server: consistent UTC format
200
+ return new Date(props.article.createdAt).toISOString().split('T')[0]
201
+ }
202
+ })
203
+ </script>
204
+ ```
205
+
206
+ ## Cause 4: Browser Extensions and Modifications
207
+
208
+ Browser extensions can inject content into the DOM.
209
+
210
+ **Mitigation:**
211
+ ```vue
212
+ <template>
213
+ <!-- Use data-allow-mismatch for areas extensions might modify -->
214
+ <head data-allow-mismatch>
215
+ <title>{{ pageTitle }}</title>
216
+ </head>
217
+ </template>
218
+ ```
219
+
220
+ ## Vue 3.5+ Suppressing Intentional Mismatches
221
+
222
+ ```vue
223
+ <template>
224
+ <!-- Suppress specific mismatch types -->
225
+ <div data-allow-mismatch="text">
226
+ {{ clientOnlyText }}
227
+ </div>
228
+
229
+ <!-- Suppress all mismatches for this element -->
230
+ <div data-allow-mismatch>
231
+ <ComplexClientComponent />
232
+ </div>
233
+ </template>
234
+ ```
235
+
236
+ Valid `data-allow-mismatch` values:
237
+ - `text` - Text content mismatches
238
+ - `children` - Child element mismatches
239
+ - `class` - Class attribute mismatches
240
+ - `style` - Style attribute mismatches
241
+ - `attribute` - Other attribute mismatches
242
+ - (no value) - All mismatches
243
+
244
+ ## Debugging Hydration Mismatches
245
+
246
+ ```javascript
247
+ // Enable detailed hydration mismatch warnings in development
248
+ // vite.config.js
249
+ export default {
250
+ define: {
251
+ __VUE_PROD_HYDRATION_MISMATCH_DETAILS__: true
252
+ }
253
+ }
254
+ ```
255
+
256
+ ```vue
257
+ <script setup>
258
+ import { onMounted } from 'vue'
259
+
260
+ // Debug: Compare server HTML with client expectation
261
+ onMounted(() => {
262
+ const serverHTML = document.getElementById('app').innerHTML
263
+ console.log('Server rendered:', serverHTML)
264
+ })
265
+ </script>
266
+ ```
267
+
268
+ ## Common Error Messages
269
+
270
+ | Error | Likely Cause |
271
+ |-------|--------------|
272
+ | "Hydration text content mismatch" | Different text on server/client (dates, random) |
273
+ | "Hydration children mismatch" | Invalid HTML nesting, conditional rendering |
274
+ | "Hydration attribute mismatch" | Dynamic attributes with different values |
275
+ | "Hydration node mismatch" | Completely different elements rendered |
276
+
277
+ ## Reference
278
+ - [Vue.js SSR Guide - Hydration Mismatch](https://vuejs.org/guide/scaling-up/ssr.html#hydration-mismatch)
279
+ - [Nuxt Hydration Best Practices](https://nuxt.com/docs/guide/best-practices/hydration)
280
+ - [data-allow-mismatch RFC](https://github.com/vuejs/core/pull/9562)
@@ -0,0 +1,256 @@
1
+ ---
2
+ title: Guard Platform-Specific APIs in Universal SSR Code
3
+ impact: HIGH
4
+ impactDescription: Accessing browser-only APIs on server causes crashes; Node.js APIs fail in browser
5
+ type: gotcha
6
+ tags: [vue3, ssr, browser-api, nodejs, universal, isomorphic, server-side-rendering]
7
+ ---
8
+
9
+ # Guard Platform-Specific APIs in Universal SSR Code
10
+
11
+ **Impact: HIGH** - SSR applications run the same code on both server (Node.js) and client (browser). Browser APIs like `window`, `document`, and `localStorage` don't exist in Node.js and will throw `ReferenceError`. Similarly, Node.js APIs like `fs` and `process` aren't available in browsers.
12
+
13
+ Universal/isomorphic code must guard platform-specific API access or use libraries that work on both platforms.
14
+
15
+ ## Task Checklist
16
+
17
+ - [ ] Never access `window`, `document`, `navigator` in `setup()` or `created()`
18
+ - [ ] Move browser API access to `onMounted()` lifecycle hook
19
+ - [ ] Use `typeof window !== 'undefined'` guard when needed outside lifecycle
20
+ - [ ] Use cross-platform libraries for common functionality (fetch, storage)
21
+ - [ ] Use Nuxt's `process.client` / `process.server` guards in Nuxt projects
22
+
23
+ ## Common Browser APIs That Break SSR
24
+
25
+ | API | Node.js Behavior |
26
+ |-----|-----------------|
27
+ | `window` | `ReferenceError: window is not defined` |
28
+ | `document` | `ReferenceError: document is not defined` |
29
+ | `localStorage` / `sessionStorage` | `ReferenceError` |
30
+ | `navigator` | `ReferenceError` |
31
+ | `location` | `ReferenceError` |
32
+ | `history` | `ReferenceError` |
33
+ | `alert` / `confirm` / `prompt` | `ReferenceError` |
34
+ | `requestAnimationFrame` | `ReferenceError` |
35
+ | `IntersectionObserver` | `ReferenceError` |
36
+ | `ResizeObserver` | `ReferenceError` |
37
+
38
+ **Incorrect - Crashes on Server:**
39
+ ```javascript
40
+ // WRONG: These run during setup/SSR - crashes in Node.js
41
+ const width = ref(window.innerWidth)
42
+ const theme = localStorage.getItem('theme')
43
+ const userAgent = navigator.userAgent
44
+ ```
45
+
46
+ ```vue
47
+ <script setup>
48
+ import { ref } from 'vue'
49
+
50
+ // WRONG: Runs on server, crashes
51
+ const scrollY = ref(window.scrollY)
52
+
53
+ // WRONG: document doesn't exist on server
54
+ document.title = 'My Page'
55
+ </script>
56
+ ```
57
+
58
+ **Correct - Use onMounted:**
59
+ ```vue
60
+ <script setup>
61
+ import { ref, onMounted, onUnmounted } from 'vue'
62
+
63
+ // Safe defaults that work on server
64
+ const width = ref(0)
65
+ const theme = ref('light')
66
+ const scrollY = ref(0)
67
+
68
+ onMounted(() => {
69
+ // Browser APIs only accessed after mount (client-only)
70
+ width.value = window.innerWidth
71
+ theme.value = localStorage.getItem('theme') || 'light'
72
+ scrollY.value = window.scrollY
73
+
74
+ // Event listeners safe in mounted
75
+ window.addEventListener('resize', handleResize)
76
+ window.addEventListener('scroll', handleScroll)
77
+ })
78
+
79
+ onUnmounted(() => {
80
+ window.removeEventListener('resize', handleResize)
81
+ window.removeEventListener('scroll', handleScroll)
82
+ })
83
+
84
+ function handleResize() {
85
+ width.value = window.innerWidth
86
+ }
87
+
88
+ function handleScroll() {
89
+ scrollY.value = window.scrollY
90
+ }
91
+ </script>
92
+ ```
93
+
94
+ **Correct - Guard with typeof:**
95
+ ```javascript
96
+ // When you need to check outside lifecycle hooks
97
+ function getStoredValue(key, defaultValue) {
98
+ if (typeof window !== 'undefined' && window.localStorage) {
99
+ return localStorage.getItem(key) ?? defaultValue
100
+ }
101
+ return defaultValue
102
+ }
103
+
104
+ // Composable with SSR awareness
105
+ export function useMediaQuery(query) {
106
+ const matches = ref(false)
107
+
108
+ // Only run on client
109
+ if (typeof window !== 'undefined') {
110
+ const mediaQuery = window.matchMedia(query)
111
+ matches.value = mediaQuery.matches
112
+
113
+ // Setup listener in lifecycle
114
+ onMounted(() => {
115
+ const handler = (e) => { matches.value = e.matches }
116
+ mediaQuery.addEventListener('change', handler)
117
+ onUnmounted(() => mediaQuery.removeEventListener('change', handler))
118
+ })
119
+ }
120
+
121
+ return matches
122
+ }
123
+ ```
124
+
125
+ ## Nuxt.js Guards
126
+
127
+ ```vue
128
+ <script setup>
129
+ // Nuxt provides process.client and process.server
130
+ if (process.client) {
131
+ // Only runs in browser
132
+ window.analytics.track('page_view')
133
+ }
134
+
135
+ if (process.server) {
136
+ // Only runs on server
137
+ console.log('Rendering on server')
138
+ }
139
+ </script>
140
+ ```
141
+
142
+ ```vue
143
+ <template>
144
+ <!-- ClientOnly component for client-only rendering -->
145
+ <ClientOnly>
146
+ <BrowserOnlyChart :data="chartData" />
147
+ <template #fallback>
148
+ <ChartSkeleton />
149
+ </template>
150
+ </ClientOnly>
151
+ </template>
152
+ ```
153
+
154
+ ## Cross-Platform Libraries
155
+
156
+ Use libraries that abstract platform differences:
157
+
158
+ ```javascript
159
+ // Fetch - works in both Node.js 18+ and browsers
160
+ const response = await fetch('/api/data')
161
+
162
+ // For older Node.js, use node-fetch or axios
163
+ import axios from 'axios'
164
+ const { data } = await axios.get('/api/data')
165
+ ```
166
+
167
+ ```javascript
168
+ // Universal cookie handling
169
+ import Cookies from 'js-cookie' // Client only
170
+ import { parse } from 'cookie' // Works both
171
+
172
+ // In Nuxt, use useCookie()
173
+ const token = useCookie('auth-token')
174
+ ```
175
+
176
+ ## Common Node.js APIs That Break in Browser
177
+
178
+ | API | Browser Behavior |
179
+ |-----|-----------------|
180
+ | `fs` | Module not found |
181
+ | `path` | Module not found |
182
+ | `process` (full) | Undefined or limited |
183
+ | `Buffer` | Undefined (unless polyfilled) |
184
+ | `__dirname` / `__filename` | Undefined |
185
+ | `require()` | Undefined in ES modules |
186
+
187
+ **Incorrect:**
188
+ ```javascript
189
+ // WRONG: Node.js APIs in universal code
190
+ import fs from 'fs'
191
+ const config = JSON.parse(fs.readFileSync('./config.json'))
192
+ ```
193
+
194
+ **Correct - Separate Server Code:**
195
+ ```javascript
196
+ // server/utils.js - Server-only file
197
+ import fs from 'fs'
198
+ export function loadConfig() {
199
+ return JSON.parse(fs.readFileSync('./config.json'))
200
+ }
201
+
202
+ // app.js - Universal code uses API instead
203
+ const config = await fetch('/api/config').then(r => r.json())
204
+ ```
205
+
206
+ ## Environment Detection Utility
207
+
208
+ ```javascript
209
+ // utils/environment.js
210
+ export const isClient = typeof window !== 'undefined'
211
+ export const isServer = !isClient
212
+
213
+ export const isBrowser = isClient && typeof document !== 'undefined'
214
+ export const isNode = typeof process !== 'undefined' &&
215
+ process.versions?.node != null
216
+
217
+ // Usage
218
+ import { isClient, isServer } from '@/utils/environment'
219
+
220
+ if (isClient) {
221
+ // Browser-specific code
222
+ }
223
+ ```
224
+
225
+ ## Third-Party Library Issues
226
+
227
+ Some libraries auto-access browser APIs on import:
228
+
229
+ ```javascript
230
+ // WRONG: Library accesses window on import
231
+ import SomeChartLibrary from 'some-chart-library'
232
+ // ^ Crashes on server if library does: const x = window.something
233
+ ```
234
+
235
+ **Correct - Dynamic Import:**
236
+ ```vue
237
+ <script setup>
238
+ import { defineAsyncComponent } from 'vue'
239
+
240
+ // Dynamic import only loads on client
241
+ const Chart = defineAsyncComponent(() =>
242
+ import('some-chart-library').then(m => m.ChartComponent)
243
+ )
244
+ </script>
245
+
246
+ <template>
247
+ <ClientOnly>
248
+ <Chart :data="data" />
249
+ </ClientOnly>
250
+ </template>
251
+ ```
252
+
253
+ ## Reference
254
+ - [Vue.js SSR - Platform-Specific APIs](https://vuejs.org/guide/scaling-up/ssr.html#access-to-platform-specific-apis)
255
+ - [Nuxt ClientOnly Component](https://nuxt.com/docs/api/components/client-only)
256
+ - [MDN: Web APIs](https://developer.mozilla.org/en-US/docs/Web/API)