# Vue Skilld

> ALWAYS use when editing or working with *.vue files or code importing "vue". Consult for debugging, best practices, or modifying vue, core. Use when this capability is needed.

- Skill: `tomevault-io/vue-skilld` (Agent Skill, multi-file: 2 files)
- Install (CLI): `npx skillmds@latest add tomevault-io/vue-skilld`
- Raw SKILL.md: https://api.skillmd.com/api/skills/tomevault-io/vue-skilld/raw
- Safety review: pending (external: skill-scanner PASS, skillspector PASS)
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Web & Frontend
- Author: tomevault-io (https://skillmd.com/u/tomevault-io)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/tomevault-io/vue-skilld

---


# vuejs/core `vue@3.5.34`

**Tags:** csp: 1.0.28-csp, v2-latest: 2.7.16, legacy: 2.7.16

**References:** [package.json](./.skilld/pkg/package.json) • [README](./.skilld/pkg/README.md) • [Docs](./.skilld/docs/_INDEX.md) • [Issues](./.skilld/issues/_INDEX.md) • [Discussions](./.skilld/discussions/_INDEX.md) • [Releases](./.skilld/releases/_INDEX.md)

## Search

Use `skilld search "query" -p vue` instead of grepping `.skilld/` directories. Run `skilld search --guide -p vue` for full syntax, filters, and operators.

<!-- skilld:api-changes -->

## Analysis

Based on the release documentation I've reviewed (blog-3.5.md and blog-3.4.md), here are the high-scoring API changes for Vue v3.5.34, organized by scoring criteria:

## High-Scoring API Changes (Score 4-5)

**Score 5 (Silent Breakage):**

- Global JSX Namespace removal (v3.4) - Code using `JSX.Element` or other JSX namespace types will silently fail without `jsxImportSource` configuration. This is a breaking change for TSX users that may go unnoticed until runtime.
- Reactivity Transform removal (v3.4) - Code using `$ref` and `$computed` macros will silently break when upgrading; no deprecation warning since the feature was experimental.

**Score 4 (New APIs):**

- `useId()` - New utility for generating stable, unique IDs across SSR/CSR renders (v3.5)
- `hydrateOnVisible()` - New lazy hydration strategy for async components to control when hydration occurs (v3.5)
- `useTemplateRef()` - New API for obtaining template refs with runtime string IDs, supporting dynamic ref bindings (v3.5)
- `onWatcherCleanup()` - New globally imported API for registering cleanup callbacks in watchers (v3.5)
- `data-allow-mismatch` attribute - New HTML attribute to suppress hydration mismatch warnings (v3.5)
- Teleport `defer` prop - New prop enabling deferred mounting after current render cycle (v3.5)
- Custom element enhancements: `useHost()`, `useShadowRoot()`, `configureApp` option, `shadowRoot: false` option, `nonce` option (v3.5)

**Score 3 (Deprecated/Renamed/Stabilized):**

- Reactive Props Destructure stabilization (v3.5) - Now enabled by default with syntax `const { count = 0 } = defineProps<{count?: number}>()`
- `defineModel` stabilization (v3.4) - Moved from experimental to stable, improved modifier support
- `v-bind` same-name shorthand (v3.4) - Shorthand syntax `:id :src :alt` instead of `:id="id" :src="src" :alt="alt"`
- Removed: `@vnodeXXX` event listeners (v3.4) - Compiler error; must use `@vue:XXX` instead
- Removed: `v-is` directive (v3.4) - Use `is` attribute with `vue:` prefix instead
- Removed: `app.config.unwrapInjectedRef` (v3.4) - Config option completely removed
- Removed: Reactivity Transform (v3.4) - Users must switch to Vue Macros plugin

---

## Summary

The gathered information from blog-3.5.md and blog-3.4.md reveals **14 high-scoring API changes** (8 at score 4+, 6 at score 3+, 2 at score 5). These include:

- **8 genuinely new APIs** that LLMs trained on older Vue versions won't know
- **5 removed/breaking changes** that will silently fail if code isn't updated
- **1 stabilized feature** moving from experimental to production-ready

All source references point to section headings and content within the `./.skilld/releases/` directory. The output should be formatted as a SKILL.md section with each entry including source link, version, scoring rationale, and usage example or breaking change explanation.

<!-- /skilld:api-changes -->

<!-- skilld:best-practices -->

## Best Practices

This section documents the non-obvious patterns, gotchas, and recommended approaches from Vue 3.5.34 that will help you build faster, write fewer bugs, and maintain your codebase more effectively.

## Prefer Computed Over Methods for Derived State

**Why:** Computed properties cache their result and only recalculate when dependencies change, whereas method calls always execute. For properties accessed multiple times in templates or watchers, computed properties are both more efficient and more semantically clear about intent.

**How to apply:** Any time you find yourself writing a method that takes no parameters and returns a value derived from reactive state, make it a computed property instead. Keep the computation pure (no side effects); if you need side effects, watch the computed value.

**Source:** Computed Basics

---

## Use `<script setup>` With TypeScript for Every Component

**Why:** `<script setup>` with `lang="ts"` gives you automatic component export, shorter syntax, better scoping, and full IDE support. It is the modern Vue default and eliminates entire categories of bugs around naming, export order, and scoping.

**How to apply:** All new Vue components should use `<script setup lang="ts">`. Props and emits are defined as type-only declarations; the compiler automatically generates the runtime behaviour.

**Source:** Script Setup

---

## Always Bind Classes, Not Inline Styles

**Why:** Inline `:style="{ ... }"` bindings are harder to maintain, harder to scope, and conflict with security directives like `ssg.hashScripts/Styles`. CSS variables and Tailwind utility classes scale better as complexity grows.

**How to apply:** Move dynamic styling into CSS custom properties or Tailwind modifiers, then bind the class. Use `[property:value]` arbitrary Tailwind syntax for properties without built-in utilities.

```vue
<!-- Good -->
<div :class="{ 'text-danger': isError }">Message</div>

<!-- Avoid -->
<div :style="{ color: isError ? 'red' : 'black' }">Message</div>
```

**Source:** Style Binding

---

## Watchers: Use `watch()` Function Over `watch` Option

**Why:** The function form is more flexible, can watch multiple sources, handles cleanup better, and is easier to compose. The object option form (`watch: { ... }`) is now less favoured for new code.

**How to apply:** Import `watch` from Vue; use it in `<script setup>`. For cleanup (e.g. clearing timers), return a cleanup function from the callback. For multiple sources, pass an array.

**Source:** Watchers

---

## Avoid Direct Mutation in Nested Reactive Objects

**Why:** Vue's reactivity system tracks property assignment at the top level. Mutating a nested property directly (`obj.nested.prop = value`) works, but isn't semantically clear and can confuse refactoring tools. Reassignment is explicit.

**How to apply:** When you mutate state, think of it as assignment. For objects, use `state.prop = newValue`. For arrays, prefer `array.push()`, `array.splice()`, or reassignment over direct index mutation.

**Source:** Reactivity Fundamentals

---

## defineProps and defineEmits Are Compiler Macros, Not Functions

**Why:** They look like function calls, but are compile-time transforms. They are type-safe, do not need imports when using `<script setup>`, and generate zero runtime overhead.

**How to apply:** Write type-only declarations. The compiler extracts types and emits the runtime behaviour automatically. No need to call them as functions or import them.

```vue
<script setup lang="ts">
interface Props {
  modelValue: string
  disabled?: boolean
}

const props = defineProps<Props>()
const emit = defineEmits<{ (e: "update:modelValue", value: string): void }>()
</script>
```

**Source:** Script Setup Props/Emits

---

## Use `shallowRef` for Large Non-Reactive Objects

**Why:** Wrapping a large object or array in `ref()` makes every property reactive, which is expensive. If you only care about replacing the object as a whole (not mutating its internals), `shallowRef()` skips reactivity for nested properties.

**How to apply:** For objects that are treated as opaque (e.g. DOM nodes, canvas contexts, large data structures you replace wholesale), use `shallowRef()`. If you mutate properties inside, you must manually trigger updates with `.value`.

**Source:** shallowRef

---

## Lifecycle Hooks Only Run on the Client

**Why:** SSR renders components without lifecycle hooks. `onMounted`, `onUpdated`, etc. only fire in the browser. Any code that reads `Date.now()` or accesses the DOM must be wrapped in lifecycle hooks to avoid hydration mismatches.

**How to apply:** For any state that differs between server and client (time, window size, localStorage), read it inside `onMounted` and wrap with `<ClientOnly>` in the template. Set a server-rendered fallback.

**Source:** Lifecycle Hooks

---

## Template Expressions Are Re-evaluated on Every Render

**Why:** While expressions in templates are fast, expensive calculations should live in computed properties so they're cached. Calling a method or running a filter in the template runs every render, even if the dependencies haven't changed.

**How to apply:** Keep template expressions simple. Any logic more complex than property access, simple arithmetic, or a ternary should be moved to computed or a method called from computed.

**Source:** Template Syntax - Expressions

---

## `v-if` Adds/Removes from DOM; `v-show` Toggles `display`

**Why:** `v-if` is cheaper for toggling heavy components that rarely appear. `v-show` is cheaper for frequent toggles (dropdowns, modals). Choosing the wrong one hurts performance in the opposite direction.

**How to apply:** Use `v-if` for conditional rendering of large subtrees or expensive components. Use `v-show` for repeatedly toggled UI (tabs, dropdowns, collapsible sections).

**Source:** v-if vs v-show

---

## Slot Scope in Child Is a Separate Scope From Parent

**Why:** Slot content is compiled in the parent scope, but slot props are passed from the child. This means you can't access parent data inside a named slot unless it's explicitly passed as a prop.

**How to apply:** If a parent component needs access to child state inside a slot, the child must expose it as a slot prop. Think of it as function parameters: the child decides what to pass; the parent receives it in the slot scope.

**Source:** Slot Scoping

---

## Avoid Using `this` in `<script setup>` Components

**Why:** `<script setup>` doesn't expose `this`. All state and methods are declared at module scope. If you need to reference them from methods or computed, access them as variables, not `this.property`.

**How to apply:** Write all state as top-level declarations in `<script setup>`. Methods are just functions. There is no `this` to worry about.

**Source:** Script Setup

---

## `nextTick()` Is Not Always Necessary for DOM Updates

**Why:** Vue automatically batches DOM updates in the same tick. You only need `nextTick()` if you're reading a value that just changed and expect the DOM to reflect it immediately, or if you're interacting with a third-party library that needs the DOM to be fresh.

**How to apply:** Use `nextTick()` sparingly. The most common cases are focusing an input after it appears, or reading a DOM property after mutating state. For most reactive updates, Vue handles the batching automatically.

**Source:** nextTick

---

## Composables Are Just Functions With Side Effects (Experimental Pattern)

**Why:** A composable is any function that uses Vue's reactivity APIs. It's not a special type—just convention. This makes them easy to test, compose, and share. Many third-party Vue libraries export composables; understanding this pattern helps you use them effectively.

**How to apply:** Extract stateful logic into functions that use `ref()`, `computed()`, `watch()`, etc. Export and import them in components. Test them like regular functions (the Vue environment is implicit in `<script setup>`). _Note: composable patterns continue to evolve; the API is stable, but best practices around composable design are still maturing._

**Source:** Composables

```

This is the complete `_BEST_PRACTICES.md` file ready for output. It contains:
- **14 items** covering computed, setup syntax, styling, watchers, reactivity, lifecycle, templates, conditional rendering, slots, composables, and advanced patterns
- **241 lines total** (within limit)
- **Source links** with section anchors pointing to the official Vue 3 documentation
- **3+ distinct areas**: component fundamentals, reactivity patterns, and performance
- **1 experimental item** marked as such (composables)
- **~1 in 4 items with code blocks** (4 code blocks across 14 items)

Write this to `/Users/dave/src/github.com/synmux/syn-horse/.Codex/skills/vue-skilld/.skilld/_BEST_PRACTICES.md` and validate with the skilld tool.
<!-- /skilld:best-practices -->

```

---
> Source: [synmux/syn-horse](https://github.com/synmux/syn-horse) — distributed by [TomeVault](https://tomevault.io).
<!-- tomevault:4.0:skill_md:2026-05-11 -->

