Claude
Skills
Sign in
Back

vue

Included with Lifetime
$97 forever

Vue 3 conventions, Composition API patterns, SFC structure, reactivity, composables, and TypeScript integration. Invoke whenever task involves any interaction with Vue code — writing, reviewing, refactoring, debugging, or understanding .vue files, composables, and Vue component architecture.

Design

What this skill does


# Vue

**Composition API is the default. `<script setup>` is the default syntax. If you reach for Options API, you need a
reason.**

Vue 3 rewards explicit, composable code. Prefer `ref()` over `reactive()`, composables over mixins, and typed props over
runtime-only validation. References contain extended examples, rationale, and edge cases for each topic.

## References

- **Reactivity** — [`${CLAUDE_SKILL_DIR}/references/reactivity.md`]: Ref unwrapping, watchers, computed edge cases
- **SFC** — [`${CLAUDE_SKILL_DIR}/references/sfc.md`]: Full compiler macros catalog, scoped styles, template refs
- **Components** — [`${CLAUDE_SKILL_DIR}/references/components.md`]: Props, emits, slots, provide/inject
- **Composables** — [`${CLAUDE_SKILL_DIR}/references/composables.md`]: Design patterns, composition, restrictions
- **TypeScript** — [`${CLAUDE_SKILL_DIR}/references/typescript.md`]: Full utility types table, generic components, event
  typing
- **Performance** — [`${CLAUDE_SKILL_DIR}/references/performance.md`]: Update optimization, large lists, profiling

## Reactivity

### Choosing a Reactive Primitive

- **`ref()`** — Default choice. Works with any value type.
- **`reactive()`** — Grouping related state when destructure is not needed.
- **`shallowRef()`** — Large immutable structures, external state integration.
- **`shallowReactive()`** — Root-level-only reactivity on objects.
- **`computed()`** — Derived state. Caches until dependencies change.

### `ref()` Is the Primary API

- Works with primitives (`string`, `number`, `boolean`).
- Can be destructured from composable returns without losing reactivity.
- Can be reassigned (`count.value = newValue`).
- Consistent `.value` access pattern everywhere in script.
- Access `.value` in script, omit in template — templates auto-unwrap top-level refs.

### `reactive()` Limitations

- Cannot hold primitives.
- Reassignment loses reactivity — `state = reactive({...})` breaks tracking.
- Destructuring primitives loses reactivity — use `toRefs()` if you must destructure.
- Do not use `reactive()` as the primary primitive. Use `ref()`.

### Ref Unwrapping Rules

- Top-level refs in templates are auto-unwrapped: `{{ count }}` works.
- Non-top-level refs in plain objects are NOT unwrapped: `{{ obj.id + 1 }}` breaks if `obj.id` is a ref. Destructure to
  top level to fix.
- Refs nested inside `reactive()` objects are unwrapped automatically.
- Refs inside reactive arrays/collections are NOT unwrapped — need `.value`.

### Computed Properties

- Keep computed getters pure — no side effects.
- Split complex computed into smaller ones.
- Computed caches its value; only recalculates when dependencies change.
- Computed stability (3.4+): only triggers effects when the returned value actually changes. Avoid returning new objects
  from computed — each new object is "different".
- Writable computed is rare — use sparingly. Requires `get`/`set` form.

### Watchers

**`watch()` vs `watchEffect()`:**

| Use `watch()` when        | Use `watchEffect()` when        |
| ------------------------- | ------------------------------- |
| Need old and new values   | Don't need old value            |
| Want lazy execution       | Want immediate execution        |
| Watching specific sources | Dependencies are implicit       |
| Need conditional watching | Effect tracks all accessed refs |

**`watch()` options:**

- `{ immediate: true }` — run callback on creation (like watchEffect).
- `{ deep: true }` — watch all nested properties (expensive, use sparingly).
- Watch a getter for specific property: `watch(() => obj.specificProp, callback)`.
- Watch multiple sources: `watch([a, b], ([newA, newB]) => {...})`.

**Cleanup:** Both `watch` and `watchEffect` support cleanup via the `onCleanup` parameter. Use it for aborting fetch
requests, clearing timers, removing listeners.

### DOM Update Timing

Reactive state changes batch DOM updates to the next tick. Use `nextTick()` for post-DOM-update logic.

## Single-File Components

### Block Order

Always: `<script setup>` first, `<template>` second, `<style>` last.

### Organization Within `<script setup>`

Order declarations logically:

1. Imports — Vue APIs, components, composables, types
2. Props and emits — `defineProps`, `defineEmits`
3. Composable calls — `useRouter()`, custom composables
4. Reactive state — `ref()`, `reactive()`, `computed()`
5. Functions — event handlers, helpers
6. Watchers — `watch()`, `watchEffect()`
7. Lifecycle hooks — `onMounted()`, `onUnmounted()`
8. Expose — `defineExpose()` (rare)

### Compiler Macros

Key macros available without import in `<script setup>`: `defineProps()`, `defineEmits()`, `defineModel()`. Use
`defineOptions()` for options that `<script setup>` doesn't natively support (`name`, `inheritAttrs: false`). Full macro
catalog in `${CLAUDE_SKILL_DIR}/references/sfc.md`.

### Template Syntax

**Directive shorthands** — use consistently, don't mix:

- **`:prop`** (`v-bind:prop`) — Bind attribute/prop
- **`@event`** (`v-on:event`) — Listen to event
- **`#slot`** (`v-slot:slot`) — Named slot

**Conditional rendering:**

- `v-if` / `v-else-if` / `v-else` for conditional blocks.
- `v-show` for frequent toggles (CSS `display: none`, avoids mount/unmount cost).
- Use `v-if` for conditions that rarely change, `v-show` for frequent toggles.

**Template refs:**

- 3.5+: `useTemplateRef<HTMLInputElement>('input')` with matching `ref` attribute.
- Pre-3.5: `ref<HTMLInputElement | null>(null)` with matching ref name.

### Scoped Styles

- Use `<style scoped>` by default. Global styles only in `App.vue` or layouts.
- Child component root elements are affected by both parent and child scoped styles.
- Deep selectors: `.parent :deep(.child-class)` to style child internals (use sparingly).
- CSS modules: `<style module>` generates unique class names, accessed via `$style`.
- `v-bind(color)` in `<style>` uses reactive values in CSS.

## Components

### Naming

- Multi-word names always — `TodoItem` not `Item`. Avoids HTML element conflicts.
- PascalCase in SFC templates: `<TodoItem />`. Kebab-case only in in-DOM templates.
- PascalCase filenames: `TodoItem.vue`.
- Base component prefix for presentational components: `BaseButton`, `BaseIcon`.
- Parent prefix for tightly coupled children: `TodoListItem`, `TodoListItemButton`.
- Highest-level word first to group related: `SearchButtonClear`, `SearchInputQuery`.
- Full words always — no abbreviations.
- One component per file. No inline registration.
- Self-closing tags for components without children: `<MyComponent />`.
- PascalCase when importing in JS/TS.

### Props

- Use `defineProps<T>()` (type-based) in TypeScript projects. Object syntax in JS projects. Never array syntax in
  committed code.
- Declare in `camelCase`, use in templates as `kebab-case` — Vue converts automatically.
- Never mutate props. Use `computed()` for transformations, `ref()` + initial value for local copies.
- One-way data flow — props down, events up.
- Reactive props destructure (3.5+): destructured props are reactive, usable in watch/computed directly.
- Pass destructured props to composables via getter: `useComposable(() => id)`.

### Emits

- Declare all emits with `defineEmits()` — preferably type-based.
- Emit in `camelCase`: `emit('someEvent')`. Listen in `kebab-case`: `@some-event="handler"`. Vue converts automatically.
- Use named tuple syntax (3.3+) for type-based emits: `defineEmits<{ change: [id: number] }>()`.

### v-model

- Use `defineModel<T>()` for two-way binding shorthand.
- Named v-model: `defineModel<string>('firstName')` with `v-model:first-name="first"` on parent.

### Slots

- Default slot: `<slot />` in child, content between tags in parent.
- Named slots: `<slot name="header" />` in child, `<template #header>` in parent.
- Scoped slots: pass data via slot props — `<slot :item="item" />`, consume with `<template #default="{ item }">`.

### Provide / Inject

- Use `Symbol` keys (`InjectionKey<T>`) for type safety — av
Files: 8
Size: 44.4 KB
Complexity: 51/100
Category: Design

Related in Design