Skip to content

/web-framework-vue-composition-api

Vue 3 Composition API patterns, reactivity primitives, composables, lifecycle hooks

shell
$ npx -y skills add agents-inc/skills --skill web-framework-vue-composition-api --agent claude-code

How it fires

How this skill gets triggered: by you, by Claude, or both.

  • Fires itselfAuto-invocation. Claude auto-loads it when your prompt matches the work.
  • You can call itInvoke it directly when you want it.
  • Slash command/web-framework-vue-composition-api
How auto-invocation works

Context preview

The summary Claude sees to decide when to auto-load this skill.

Vue 3 Composition API patterns, reactivity primitives, composables, lifecycle hooks

SKILL.md

web-framework-vue-composition-api.SKILL.md
name: web-framework-vue-composition-api
description: Vue 3 Composition API patterns, reactivity primitives, composables, lifecycle hooks

Vue 3 Composition API

> **Quick Guide:** Use `<script setup>` for all components. `ref()` for primitives, `reactive()` for objects. Extract reusable logic into composables (`use*` functions). Clean up side effects in `onUnmounted`. Use `defineModel()` for v-model (3.4+), `useTemplateRef()` for DOM refs (3.5+), `onWatcherCleanup()` to cancel stale async work (3.5+). Destructured props require getter wrappers in `watch()`.

---

<critical_requirements>

CRITICAL: Before Using This Skill

> **All code must follow project conventions in CLAUDE.md** (kebab-case, named exports, import ordering, `import type`, named constants)

**(You MUST use `<script setup>` syntax for all new Vue components)**

**(You MUST clean up all side effects (timers, listeners, subscriptions) in `onUnmounted`)**

**(You MUST use `ref()` for primitives and `reactive()` for objects - access ref values via `.value`)**

**(You MUST prefix all composable functions with `use` following Vue conventions)**

**(You MUST wrap destructured props in a getter for `watch()` - `watch(() => count, ...)` not `watch(count, ...)`)**

</critical_requirements>

---

**Auto-detection:** Vue 3 Composition API, script setup, ref, reactive, computed, watch, watchEffect, composables, onMounted, onUnmounted, defineProps, defineEmits, defineExpose, defineModel, useTemplateRef, useId, onWatcherCleanup, provide, inject, Suspense

**When to use:**

  • Building Vue 3 components using Composition API
  • Creating reusable composables (use\* functions)
  • Managing reactive state with ref/reactive
  • Handling component lifecycle and side effects
  • TypeScript integration with Vue components

**Key patterns covered:**

  • Script setup syntax and compiler macros (defineProps, defineEmits, defineExpose)
  • Reactivity primitives (ref, reactive, computed, watch, watchEffect)
  • Composables pattern for logic reuse
  • defineModel() for v-model binding (Vue 3.4+)
  • useTemplateRef(), useId(), onWatcherCleanup() (Vue 3.5+)
  • Reactive props destructure with getter requirement (Vue 3.5+)
  • Provide/Inject for dependency injection
  • Async components and Suspense

**When NOT to use:**

  • Components that don't benefit from logic extraction
  • When team has no Composition API experience (consider gradual adoption)

---

<philosophy>

Philosophy

The Composition API enables organizing code by **logical concern** rather than by option type (data, methods, computed). This makes complex components more maintainable and enables powerful logic reuse through composables.

**Core principles:**

1. **Composition over configuration** - Group related logic together instead of splitting across options 2. **Explicit reactivity** - State is explicitly reactive via `ref()` and `reactive()` 3. **Logic reuse via composables** - Extract and share stateful logic between components 4. **TypeScript-first** - Types flow naturally without excessive annotations

</philosophy>

---

<patterns>

Core Patterns

Pattern 1: Script Setup with Props and Emits

All variables/functions in `<script setup>` are automatically available in the template. Use TypeScript generics with `defineProps` and `defineEmits` for type-safe interfaces.

<script setup lang="ts">
import { ref, computed } from "vue";

const props = defineProps<{
  userId: string;
  initialCount?: number;
}>();

const emit = defineEmits<{
  update: [value: number];
  submit: [];
}>();

const count = ref(props.initialCount ?? 0);
const doubleCount = computed(() => count.value * 2);

function increment() {
  count.value++;
  emit("update", count.value);
}
</script>

**Why good:** No explicit return needed, TypeScript types flow naturally, named tuple emit syntax (Vue 3.3+) self-documents payloads

See [examples/core.md](examples/core.md) for a complete component with loading/error handling.

---

Pattern 2: Reactivity - ref vs reactive

`ref()` for primitives and reassignable values, `reactive()` for objects with nested properties. Access ref values via `.value` in script; templates unwrap automatically.

const count = ref(0); // Primitive -> ref
count.value++; // .value in script

const state = reactive({
  // Nested object -> reactive
  user: null as User | null,
  settings: { theme: "light" },
});
state.settings.theme = "dark"; // Direct access, no .value

**Gotcha:** Destructuring `reactive()` loses reactivity - use `toRefs(state)` if you need to destructure.

See [examples/reactivity.md](examples/reactivity.md) for ref/reactive/computed patterns and anti-patterns.

---

Pattern 3: Watch and WatchEffect

**Skip if using Nuxt — use useFetch or useAsyncData instead.**

`watch()` for explicit sources with access to old values. `watchEffect()` for automatic dependency tracking that runs immediately. Use `onWatcherCleanup()` (Vue 3.5+) to cancel stale async work.

// watch: explicit source, access to old value
watch(searchQuery, async (newQuery, oldQuery) => {
  /* ... */
});

// watchEffect: auto-tracks dependencies, runs immediately
watchEffect(async () => {
  if (userId.value) userData.value = await fetchUser(userId.value);
});

// Cleanup: cancel stale requests (Vue 3.5+)
watch(searchQuery, async (query) => {
  const controller = new AbortController();
  onWatcherCleanup(() => controller.abort());
  const res = await fetch(`/api/search?q=${query}`, {
    signal: controller.signal,
  });
});

**Gotcha:** Watch reactive object properties with a getter: `watch(() => state.count, ...)` not `watch(state.count, ...)`.

See [examples/vue-3-5-features.md](examples/vue-3-5-features.md) for complete onWatcherCleanup patterns.

---

Pattern 4: Lifecycle and Cleanup

Always pair `onMounted` setup with `onUnmounted` cleanup. Timers, listeners, observers, WebSockets - anything opened must be closed.

const POLL_INTERVAL_MS = 5000;
l
Read more
Read it on GitHub ↗

Showing the first part of this file.

Ships withagents-inc-skills

The official skills marketplace for Agents Inc. 150+ skills covering everything from React and Prisma to Redis, ElevenLabs, and infrastructure tooling. Pick the skills that match your stack and install them via Claude Code. Need more control?

Get the whole plugin, auto-invoked