From vue-best-practices
Vue 3 + TypeScript best practices. Load for any .vue file, composable, Pinia store, or Vue-related task in any Vue 3 project. Covers component decomposition, composable patterns, provide/inject, Pinia, reactivity, SFC structure, and file size and architecture rules. Based on vuejs-ai/skills vue-best-practices.
How this skill is triggered — by the user, by Claude, or both
Slash command
/vue-best-practices:vue-best-practicesThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
Stack: Vue 3 + `<script setup lang="ts">` + Pinia + Vitest. Read this before writing any `.vue` file or composable.
Stack: Vue 3 + <script setup lang="ts"> + Pinia + Vitest. Read this before writing any .vue file or composable.
computed..vue file is approaching 500 lines, look for what to extract next.Before writing any non-trivial component, sketch the component map:
components/<Feature>/, composables/use<Feature>.ts.Split a component when any of these are true:
Don't extract when:
Read references/reactivity.md before working with reactive state.
Key rules:
shallowRef() for primitives, ref() when you replace the whole value, reactive() when you mutate in place.computed() for all derived values — never watchEffect + manual assignment.watch().immediate: true instead of duplicating initial calls in onMounted.shallowRef for objects, call triggerRef() after mutating nested properties.Read references/sfc.md before writing .vue files.
Key rules:
<script setup> → <template> → <style scoped>.<style scoped> for component styles; class selectors only (not element selectors).v-if and v-for on the same element — use a computed filter.:key in v-for — prefer item.id, never index for stateful lists.Read references/component-data-flow.md before wiring components together.
Key rules:
InjectionKey<T> is the right pattern when a subtree needs shared context without prop drilling — not 40 props. See references/component-data-flow.md for the typed pattern.defineProps<Props>(), defineEmits<Emits>(), InjectionKey<T>.v-model via defineModel() for two-way bindings (Vue 3.4+).Recommended provide/inject pattern:
// useXxxContext.ts
import type { InjectionKey } from 'vue'
export interface XxxContext { /* all state + actions the subtree needs */ }
export const XXX_CTX_KEY: InjectionKey<XxxContext> = Symbol('xxxContext')
export function provideXxxContext(ctx: XxxContext) {
provide(XXX_CTX_KEY, ctx)
}
export function useXxxContext(): XxxContext {
const ctx = inject(XXX_CTX_KEY)
if (!ctx) throw new Error('useXxxContext must be used inside XxxProvider')
return ctx
}
Use this pattern whenever a parent component owns state that multiple children need — instead of passing it as props.
Read references/composables.md before writing useX functions.
Key rules:
utils/, not composables.readonly() state and explicit action functions — consumers should not mutate composable internals directly.Large modal layers extracted from view components use the provide/inject pattern:
useXxxModalContext.ts — typed InjectionKey<XxxModalContext> with provideXxxModalContext and useXxxModalContext.XxxModalsLayer.vue — zero props, uses useXxxModalContext() to inject all state. Contains all modal/dialog/overlay declarations for that view.provideXxxModalContext(...) and renders <XxxModalsLayer /> — no modal imports needed in the view itself.This keeps view components thin and makes the modal layer independently readable.
ref/reactive or composables.shallowRef + triggerRef for large normalized maps for performance.storeToRefs() — reactivity breaks.Every hardcoded value needs a named constant — including:
const TAB_SWITCH_FIT_VIEW_DELAY_MS = 50)Run the project's test command (e.g. npm test, npm run test:unit, vitest) after every change. All tests must pass before committing.
setActivePinia(createPinia()) in beforeEach, localStorage.clear() if the store reads from localStorage on init.Creates, edits, and optimizes skills for Claude Code, including drafting, evaluating with test prompts, iterating on performance, and improving skill descriptions for better triggering accuracy.
npx claudepluginhub signalcanvas/signalcanvas-skills --plugin vue-best-practices