Vue 3 Production Patterns
Pathrule2 Rules • 3 Memories • 1 Skill
Vue applications usually fail at production boundaries rather than at template syntax: destructured reactive values stop updating, watchers become hidden command buses, and server-rendered markup diverges from the browser on first paint. This pattern places reactivity constraints in component code, composable ownership decisions in the composables directory, Pinia store boundaries in the store layer, and a repeatable hydration review at the workspace root. It complements the Nuxt pattern without duplicating routing or deployment guidance, and it differs from React state guidance by focusing on refs, proxies, effect scopes, and Vue's dependency tracking model.
Suggested path map
Pathrule places each piece on the matching path, so your assistant only sees it where it belongs. This is the scoping you get on import; you can adjust it in your workspace.
Rules
2Preserve ref identity across component boundaries/src/componentshighstrictKeep values reactive through props, composables, and templates instead of copying or destructuring proxies into stale locals.
| 1 | Vue tracks access through refs and reactive proxies. Copying a property into a plain local severs that access path, so the template keeps rendering an old value even though the source object changed. |
| 2 | |
| 3 | - Use `toRef` or `toRefs` when a property must be passed independently; do not destructure a reactive object into plain variables and assume dependency tracking survives. |
| 4 | - Treat props as readonly inputs. Emit an event or expose a model binding for changes instead of mutating nested prop state through a reference the parent also owns. |
| 5 | - Use `computed` for derived values and keep the getter pure. A computed getter that writes another ref creates an order-dependent loop and hides the true owner of state. |
| 6 | - Unwrap refs in templates, but keep `.value` explicit in TypeScript code so assignments and dependency reads remain visible during review. |
| 7 | |
| 8 | See /src/composables for the adjacent decision or procedure that completes this constraint. |
Keep server and browser renders deterministic/src/componentshighstrictRender identical initial markup on server and client, then introduce browser-only state after mount behind an explicit boundary.
| 1 | Hydration assumes the browser receives the same tree the server produced. Reading time, randomness, local storage, viewport size, or DOM state during setup makes the first client render disagree before Vue can attach listeners. |
| 2 | |
| 3 | - Do not call `Date.now`, `Math.random`, storage APIs, or layout measurement while producing SSR markup unless the value is serialized from the server and reused by the client. |
| 4 | - Move DOM-dependent work to `onMounted` and render a stable placeholder before mount. The placeholder must preserve structure where layout shift matters. |
| 5 | - Never keep request-specific mutable state in a module singleton on the server. Create stores and app state per request so one user's data cannot leak into another render. |
| 6 | - Use stable keys that come from data identity. Generating keys during render changes node correspondence and can silently attach state to the wrong row. |
| 7 | |
| 8 | See /src/stores for the adjacent decision or procedure that completes this constraint. |
Memories
3A composable owns one lifecycle and one state boundary/src/composablesComposables expose a focused capability, register cleanup with their caller, and avoid becoming application-wide service locators.
| 1 | A useful composable is not merely a function whose name starts with `use`. Its value is lifecycle ownership: it creates state, subscribes to an external source, and guarantees that the subscription ends with the component scope that requested it. |
| 2 | |
| 3 | - Register event listeners, observers, timers, and subscriptions inside the composable, then clean them with `onScopeDispose` so nested effect scopes are safe too. |
| 4 | - Return readonly refs for state consumers should observe but not mutate. Expose named commands for transitions that require validation or side effects. |
| 5 | - Accept refs or getters when an input can change. Normalize them at the edge rather than reading an initial value and freezing the composable to it. |
| 6 | - Split network caching, domain state, and DOM integration into separate composables; one `useApp` object that exposes everything has no enforceable ownership boundary. |
| 7 | |
| 8 | See /src/components for the rule or workflow that puts this decision into practice. |
Watchers are integration tools, not derivation tools/src/componentsUse watchers for external effects and cancellation; use computed values for anything that can be expressed from reactive inputs.
| 1 | A watcher is appropriate when a reactive change must cross Vue's boundary into an external system, such as updating a URL, starting a request, or writing storage. It is a poor substitute for a value that can be derived synchronously. |
| 2 | |
| 3 | - Prefer `computed` when the output is another value. It caches by dependency and cannot drift out of sync with its inputs. |
| 4 | - When a watcher starts asynchronous work, register cleanup and abort the prior operation before issuing the next one so late responses cannot overwrite newer state. |
| 5 | - Choose `watch` when the dependency list should be explicit and `watchEffect` only when automatic dependency discovery is genuinely clearer. |
| 6 | - Avoid deep watchers over large objects. Watch the specific scalar or immutable reference that represents the change instead of traversing the whole graph. |
| 7 | |
| 8 | See /src/composables for the rule or workflow that puts this decision into practice. |
Skills
1review-vue-reactivity-boundaries/rootAudit a Vue change for lost ref identity, runaway effects, store duplication, and hydration drift before release.
| 1 | --- |
| 2 | name: review-vue-reactivity-boundaries |
| 3 | description: Review Vue component, composable, store, and SSR boundaries after a feature or refactor. |
| 4 | --- |
| 5 | |
| 6 | # Review Vue Reactivity Boundaries |
| 7 | |
| 8 | Run this procedure when the affected surface changes, before the result is promoted to production. Record evidence for every step instead of accepting a plausible-looking result. |
| 9 | |
| 10 | - [ ] Trace each displayed value back to its ref, reactive proxy, computed getter, or serialized server input; flag every plain copy that can become stale. |
| 11 | - [ ] List every watcher and confirm it crosses into an external side effect, cancels prior async work, and cannot be replaced by a computed value. |
| 12 | - [ ] Inspect composables for listener, timer, observer, and subscription cleanup through the active effect scope. |
| 13 | - [ ] Compare server and first-client inputs for time, randomness, storage, viewport, and request-specific state; render a deterministic placeholder where needed. |
| 14 | - [ ] Run the changed flow through navigation and component teardown, then verify that Pinia and query caches have one owner for each piece of shared data. |
| 15 | |
| 16 | ## Exit criteria |
| 17 | |
| 18 | The change is complete only when the expected behavior, failure behavior, and rollback path have all been exercised with representative data. Preserve the evidence with the change so the next operator can repeat the same checks. |
Why this pattern
AI agents often destructure reactive objects, use watchers for derived state, or read browser-only values during Vue SSR, producing stale UI and hydration warnings that appear far from the change.
Built for Vue teams shipping component libraries, SPAs, or server-rendered applications with TypeScript.
Keeps your assistant from:
- Losing reactivity by destructuring a reactive proxy
- Using watchers to synchronize values that should be computed
- Sharing mutable module state across server requests
- Hydrating markup that depends on browser-only data
- License
- Apache-2.0
- Version
- 1.0.0
- Updated
- 2026-08-25