
Vue Expert Js
Implement Vue 3 script-setup components with typed props, validated emits, and single or multiple v-model bindings.
Overview
Vue Expert JS is an agent skill for the Build phase that guides Vue 3 script-setup props, emits, and v-model patterns in JavaScript projects.
Install
npx skills add https://github.com/jeffallan/claude-skills --skill vue-expert-jsWhat is this skill?
- defineProps patterns: required strings, defaults, array/object defaults, and validator-enforced enums
- defineEmits with simple event lists or per-event payload validation
- Single v-model via modelValue and update:modelValue
- Multiple named v-models (e.g. firstName, lastName) with defineProps/defineEmits pairing
- JSDoc @typedef on props for JS projects without TypeScript
Adoption & trust: 2.3k installs on skills.sh; 9.7k GitHub stars; 3/3 security scanners passed (skills.sh audits).
What problem does it solve?
Your agent keeps generating Vue components with sloppy props defaults, untyped emits, or broken v-model wiring in plain JS SFCs.
Who is it for?
Solo builders on Vue 3 + script setup who want consistent component APIs and v-model without migrating the repo to TypeScript.
Skip if: Nuxt server routes, Pinia store design, or teams standardized on Vue 2 Options API only.
When should I use this skill?
When implementing or reviewing Vue 3 script-setup components that need props, emits, or v-model in JavaScript.
What do I get? / Deliverables
You get copy-ready script-setup blocks for props, validated emits, and single or multi v-model that match Vue 3 Composition API expectations.
- Vue SFC script blocks for props, emits, and v-model
- Validated component event contracts
Recommended Skills
Journey fit
Vue component patterns are core frontend construction during the build phase when solo builders ship UI in JavaScript rather than TypeScript-first stacks. Frontend subphase is the canonical home for props, emits, and v-model architecture in Vue SFCs using defineProps and defineEmits.
How it compares
Reference skill for Vue SFC contracts—not a replacement for eslint-plugin-vue or a full UI kit generator.
Common Questions / FAQ
Who is vue-expert-js for?
Indie frontend developers using AI coding agents to write or review Vue 3 single-file components in JavaScript with Composition API and script setup.
When should I use vue-expert-js?
Use it in Build/frontend whenever you add components, fix prop/default bugs, or implement v-model and emit validation in Vue SFCs.
Is vue-expert-js safe to install?
It is documentation-only patterns with no network or shell requirements; still review the Security Audits panel on this page for the upstream package.
SKILL.md
READMESKILL.md - Vue Expert Js
# Component Architecture --- ## Props ```vue <script setup> /** * @typedef {Object} Props * @property {string} title - Required * @property {string} [subtitle] - Optional * @property {number} [count=0] - With default */ const props = defineProps({ title: { type: String, required: true }, subtitle: { type: String, default: '' }, count: { type: Number, default: 0 }, // Complex types items: { type: Array, default: () => [] }, user: { type: Object, required: true }, // Validator size: { type: String, default: 'medium', validator: (v) => ['small', 'medium', 'large'].includes(v) } }) </script> ``` --- ## Emits ```vue <script setup> const emit = defineEmits(['update', 'delete', 'close']) // With validation const emit = defineEmits({ /** @param {string} value */ update: (value) => typeof value === 'string', /** @param {{ id: number }} payload */ delete: (payload) => typeof payload?.id === 'number', close: null }) // Usage emit('update', 'new value') emit('delete', { id: 1 }) </script> ``` --- ## v-model ```vue <!-- Single v-model --> <script setup> const props = defineProps({ modelValue: { type: String, required: true } }) const emit = defineEmits(['update:modelValue']) </script> <template> <input :value="modelValue" @input="emit('update:modelValue', $event.target.value)" /> </template> ``` ```vue <!-- Multiple v-models: v-model:firstName, v-model:lastName --> <script setup> defineProps({ firstName: String, lastName: String }) defineEmits(['update:firstName', 'update:lastName']) </script> ``` --- ## Slots ```vue <!-- Card.vue --> <template> <div class="card"> <header v-if="$slots.header"><slot name="header" /></header> <div class="card-body"><slot /></div> <footer v-if="$slots.footer"><slot name="footer" /></footer> </div> </template> ``` ```vue <!-- Scoped slot --> <template> <ul> <li v-for="(item, index) in items" :key="item.id"> <slot name="item" :item="item" :index="index"> {{ item.name }} </slot> </li> </ul> </template> <!-- Usage --> <DataList :items="users"> <template #item="{ item, index }"> {{ index + 1 }}. {{ item.name }} </template> </DataList> ``` --- ## Provide / Inject ```vue <!-- Provider.vue --> <script setup> import { provide, ref, readonly } from 'vue' const theme = ref('light') provide('theme', readonly(theme)) provide('setTheme', (t) => { theme.value = t }) </script> ``` ```vue <!-- Consumer.vue --> <script setup> import { inject, ref } from 'vue' const theme = inject('theme', ref('light')) const setTheme = inject('setTheme', () => {}) </script> ``` ```javascript // Composable pattern // composables/useTheme.js import { ref, provide, inject, readonly, computed } from 'vue' const ThemeSymbol = Symbol('theme') export function provideTheme(initial = 'light') { const theme = ref(initial) const isDark = computed(() => theme.value === 'dark') const toggle = () => { theme.value = theme.value === 'light' ? 'dark' : 'light' } provide(ThemeSymbol, { theme: readonly(theme), isDark, toggle }) return { theme, isDark, toggle } } export function useTheme() { const ctx = inject(ThemeSymbol) if (!ctx) throw new Error('useTheme requires ThemeProvider') return ctx } ``` --- ## Dynamic Components ```vue <script setup> import { shallowRef, markRaw } from 'vue' import TabHome from './TabHome.vue' import TabProfile from './TabProfile.vue' const tabs = [ { name: 'Home', component: markRaw(TabHome) }, { name: 'Profile', component: markRaw(TabProfile) } ] const currentTab = shallowRef(tabs[0].component) </script> <template> <button v-for="tab in tabs" :key="tab.name" @click="currentTab = tab.component"> {{ tab.name }} </button> <KeepAlive> <component :is="currentTab" /> </KeepAlive> </template> ``` ```javascript // Async component import { defineAsyncComponent } from 'vue' const AsyncModal = defineAsyncComponent({ loader: () => import('./Modal.vue'), delay: 200,