From qe-framework
Enforces Vue 3 best practices: Composition API, TypeScript, SFC structure, component planning, and reactivity. Activates for .vue files, Vue Router, Pinia, or Vite with Vue.
How this skill is triggered — by the user, by Claude, or both
Slash command
/qe-framework:Qvue-best-practicesThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
Use this skill as an instruction set. Follow the workflow in order unless the user explicitly asks for a different order.
LICENSE.mdSYNC.mdreferences/animation-class-based-technique.mdreferences/animation-state-driven-technique.mdreferences/component-async.mdreferences/component-data-flow.mdreferences/component-fallthrough-attrs.mdreferences/component-keep-alive.mdreferences/component-slots.mdreferences/component-suspense.mdreferences/component-teleport.mdreferences/component-transition-group.mdreferences/component-transition.mdreferences/composables.mdreferences/directives.mdreferences/perf-avoid-component-abstraction-in-lists.mdreferences/perf-v-once-v-memo-directives.mdreferences/perf-virtualize-large-lists.mdreferences/plugins.mdreferences/reactivity.mdUse this skill as an instruction set. Follow the workflow in order unless the user explicitly asks for a different order.
<script setup lang="ts">.vue-options-api-best-practices skill if available.vue-jsx-best-practices skill if available.references/reactivity.mdreferences/sfc.mdreferences/component-data-flow.mdreferences/composables.mdCreate a brief component map before implementation for any non-trivial feature.
components/<feature>/..., composables/use<Feature>.ts) when adding more than one component.These are essential, must-know foundations. Apply all of them in every Vue task using the core references already loaded in section 1.1.
1.1: reactivityref/reactive), derive everything possible with computed.1.1: sfc<script> → <template> → <style>.v-html, list rendering, conditional rendering choices).Split a component when it has more than one clear responsibility (e.g. data orchestration + UI, or multiple independent UI sections).
useXxx()).Apply objective split triggers. Split the component if any condition is true:
Entry/root and route view rule:
1.1: component-data-flowv-model only for true two-way component contracts.defineProps, defineEmits, and InjectionKey as needed.1.1: composablesDo not add these by default. Load the matching reference only when the requirement exists.
<KeepAlive> for stateful view caching -> component-keep-alive<Teleport> for overlays/portals -> component-teleport<Suspense> for async subtree fallback boundaries -> component-suspense<Transition> for enter/leave effects -> transition<TransitionGroup> for animated list mutations -> transition-groupUse these only when there is explicit product or technical need.
Performance work is a post-functionality pass. Do not optimize before core behavior is implemented and verified.
Reference: See references/reactivity.md, references/perf-v-once-v-memo-directives.md, references/perf-virtualize-large-lists.md
// PATTERN: Prefer computed for derivation; use watch for side effects only
const count = shallowRef(0)
// ✅ Computed: pure derivation, cached
const doubled = computed(() => count.value * 2)
// ✅ Watch: side effects triggered by change
watch(count, (val) => console.log('Count changed to:', val))
// ❌ WRONG: watch for derivation loses caching
const tripled = ref(0)
watch(count, (val) => { tripled.value = val * 3 })
// ✅ Wrap composables in error boundary
import { onErrorCaptured } from 'vue'
export function useApiCall() {
const error = ref(null)
onErrorCaptured((err) => {
error.value = err.message
return false // suppress further propagation
})
return { error }
}
<!-- ✅ PATTERN: v-memo for selection changes + virtual scroll for 1000+ items -->
<template>
<VirtualScroller :list="items" #default="{ item }">
<div v-memo="[item.id === selectedId]" :class="{ selected: item.id === selectedId }">
<ExpensiveComponent :data="item" />
</div>
</VirtualScroller>
</template>
<script setup>
// Reference: perf-virtualize-large-lists.md
const selectedId = ref(null)
const items = ref(/* 10000+ items */)
</script>
/**
* useApiCall - Manage async data fetching with error handling
* @param {string} url - API endpoint
* @param {UseApiCallOptions} options - Configuration object
* @returns {Object} { data, loading, error, refetch }
*
* @example
* const { data, loading, error } = useApiCall('/api/users', { immediate: true })
*
* @see composables.md for composable organization patterns
*/
export function useApiCall(url, options = {}) {
// implementation
}
Apply these linters to Vue projects:
vue/multi-word-component-names, vue/no-v-html-unsafe, vue/no-unused-varsstrictTemplates: true).vue to parser configRun before commit:
eslint src --ext .vue --fix && vue-tsc --noEmit && prettier --write src
v-html with user input. Sanitize via DOMPurify or server-side HTML escapev-bind on computed templates. Use <component :is="dynamicComponentRef" />npm audit weekly. Lock vulnerable transitive deps in package-lock.jsonSee full details: references/component-data-flow.md, references/reactivity.md, references/composables.md
| Pattern | WRONG | CORRECT |
|---|---|---|
| Destructuring reactive() | const { count } = reactive({count: 0}) | const { count } = toRefs(state) |
| Watch non-getter | watch(state.count, ...) | watch(() => state.count, ...) |
| Watcher derivation | const doubled = ref(0); watch(..., () => { doubled.value = ... }) | const doubled = computed(...) |
| Direct Pinia mutation | store.items.push(...) (no action) | Define action: store.addItem(item) |
| v-html with user data | <div v-html="userComment" /> | Use DOMPurify: v-html="sanitize(userComment)" |
npx claudepluginhub inho-team/qe-framework --plugin qe-frameworkCreates, edits, and optimizes skills for Claude Code, including drafting, evaluating with test prompts, iterating on performance, and improving skill descriptions for better triggering accuracy.