Vue
SkillAI & modelsUse when building Vue 3 applications. Covers the Composition API, reactivity fundamentals, composables, Pinia state management, and the reactivity mistakes that cause silent update failures.
Available today. Use it from your connected AI after setup.
No other account needed.
Connect ahel once, and every AI you use reads what you have installed.
Then ask your AI: use the Vue skill
What this skill tells your AI
The instructions your AI receives, as published by nimadorostkar/claude-skills-collection in skills/frontend/vue/SKILL.md and read by ahel’s review.
Purpose
Write Vue 3 with a correct mental model of reactivity. Most Vue bugs are not logic errors — they are a ref that was destructured, or a reactive object that was reassigned, and the view silently stopped updating.
When to Use
- Building or reviewing Vue 3 applications.
- Extracting reusable logic into composables.
- Debugging a value that changes but does not re-render.
- Structuring state with Pinia.
Capabilities
- Composition API with
<script setup>. - Reactivity:
ref,reactive,computed,watch,watchEffect, and their differences. - Composables for shared logic with proper lifecycle cleanup.
- Pinia stores, including typed state and actions.
- Performance:
shallowRef,v-memo, and avoiding deep watchers.
Inputs
- The component or composable, and where its state originates.
- The reactivity symptom, if debugging.
Outputs
- Components using
<script setup>with typed props and emits. - Composables that clean up after themselves.
- Reactivity that survives destructuring and reassignment.
Workflow
- Prefer
refoverreactive—refsurvives destructuring (via.value) and reassignment.reactiveloses reactivity on both, silently. - Derive with
computed— Not with awatchthat assigns to another ref. That is two renders and a chance to be out of sync. - Extract composables for shared behavior — Anything that owns a subscription, listener, or timer must clean it up in
onScopeDisposeoronUnmounted. - Type the boundaries —
defineProps<T>()anddefineEmits<T>()with type arguments. Runtime-only prop declarations discard type information. - Watch narrowly — Watch a specific getter, not a whole object with
deep: true. Deep watchers on large objects are a common and invisible performance cost.
Best Practices
- Destructuring a
reactiveobject breaks reactivity:const { count } = reactive({ count: 0 })gives you a plain number. UsetoRefsor, better, useref. - Reassigning a
reactiveobject (state = { ... }) replaces the proxy and detaches every existing binding.refdoes not have this failure mode. watchEffecttracks whatever it reads — including things you did not intend.watchwith an explicit source is more predictable and should be the default.- A composable that adds an event listener without removing it leaks on every mount.
onScopeDisposehandles the composable-in-composable case thatonUnmounteddoes not. - Prefer
shallowReffor large immutable data structures; deep reactivity on a 10,000-element array is a real cost paid on every mutation. - Do not mutate props. Emit an event and let the owner change its own state.
Examples
A composable with correct cleanup and derived state:
export function useOrderStream(orderId: Ref<string>) {
const events = ref<OrderEvent[]>([]);
const status = computed(() => events.value.at(-1)?.status ?? "unknown");
const error = ref<Error | null>(null);
let source: EventSource | null = null;
const connect = (id: string) => {
source?.close();
events.value = [];
source = new EventSource(`/api/orders/${id}/stream`);
source.onmessage = (e) => events.value.push(JSON.parse(e.data));
source.onerror = () => { error.value = new Error("stream disconnected"); };
};
watch(orderId, connect, { immediate: true });
onScopeDispose(() => source?.close()); // fires even when used inside another composable
return { events, status, error };
}
Reactivity that silently fails:
// Broken: `count` is a plain number; the template never updates.
const state = reactive({ count: 0 });
const { count } = state;
// Broken: reassignment detaches every binding to the old proxy.
let state = reactive({ items: [] });
state = reactive({ items: newItems });
// Correct: refs survive both.
const count = ref(0);
const items = ref<Item[]>([]);
items.value = newItems;
Notes
v-memoskips re-rendering a subtree when its dependency array is unchanged. It is worth reaching for on largev-forlists and almost nowhere else.- Pinia stores are reactive objects: destructuring them has the same failure mode as
reactive. UsestoreToRefs. <script setup>compiles props and emits at build time. The runtimedefineComponentform still works but discards the type-level checking that makes Vue 3 pleasant.
Signals
- GitHub stars
- 26
- Forks
- 3
- Last commit
- Aug 2026
Advanced
- Catalog kind
- skill
- Gateway key
vue-nimadorostkar- Source
- github.com/nimadorostkar/claude-skills-collection