Vue
Skill nimadorostkar/Claude-Skills-collection/skills/frontend/vue
Use when building Vue 3 applications. Covers the Composition API, reactivity fundamentals, composables, Pinia state management, and the reactivity mistakes that cause silent update failures.From its SKILL.md
npx -y skills add nimadorostkar/Claude-Skills-collection --skill vueAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
SKILL.md
4.6 KB, ~1.0k tokens by cl100k_base, as published. Nobody here has run it
Vue
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.
What ships with it
Read from the repository
Just SKILL.md. No reference files, no scripts.