--- name: vue-component-patterns user-invocable: false description: Use when Vue component patterns including props, emits, slots, and provide/inject. Use when building reusable Vue components. allowed-tools: - Bash - Read --- # Vue Component Patterns Master Vue component patterns to build reusable, maintainable components with proper prop validation, events, and composition. ## Props Patterns ### Basic Props with TypeScript ```typescript {{ title }} Count: {{ count }} {{ item }} ``` ### Advanced Prop Types ```typescript ``` ### Runtime Props Validation ```typescript ``` ### Props with Defaults ```typescript ``` ## Emits Patterns ### TypeScript Emits ```typescript ``` ### Runtime Emits Validation ```typescript ``` ### Custom v-model ```typescript ``` ### Multiple v-models ```typescript ``` ## Slots Patterns ### Basic Slots ```typescript Card Title Card content goes here Action ``` ### Scoped Slots ```typescript {{ index + 1 }}. {{ item.name }} - {{ item.email }} ``` ### Fallback Slot Content ```typescript Click Me Custom Text ``` ### Dynamic Slots ```typescript ``` ### Renderless Components with Slots ```typescript Mouse position: {{ x }}, {{ y }} ``` ## Provide and Inject for Deep Passing ### Basic Provide/Inject ```typescript Toggle Theme ``` ### Type-Safe Provide/Inject ```typescript // types.ts import type { InjectionKey, Ref } from 'vue'; export interface AppConfig { apiUrl: string; timeout: number; } export interface User { id: number; name: string; email: string; } export const ConfigKey: InjectionKey = Symbol('config'); export const UserKey: InjectionKey> = Symbol('user'); // Provider // Consumer ``` ### Provide/Inject with Reactivity ```typescript Count: {{ state.count }} Increment ``` ## Component Registration ### Global Registration ```typescript // main.ts import { createApp } from 'vue'; import App from './App.vue'; import BaseButton from './components/BaseButton.vue'; import BaseInput from './components/BaseInput.vue'; const app = createApp(App); // Register globally app.component('BaseButton', BaseButton); app.component('BaseInput', BaseInput); app.mount('#app'); // Use anywhere without importing Click ``` ### Local Registration ```typescript Click ``` ### Auto-Import Components ```typescript // vite.config.ts import { defineConfig } from 'vite'; import vue from '@vitejs/plugin-vue'; import Components from 'unplugin-vue-components/vite'; export default defineConfig({ plugins: [ vue(), Components({ // Auto import from components directory dirs: ['src/components'], // Generate types dts: true }) ] }); // Now use components without importing No import needed! ``` ## Async Components ```typescript Loading... ``` ## Teleport for Modals and Portals ```typescript Open Modal Modal Content This is teleported to body! ``` ## KeepAlive for Component Caching ```typescript {{ tab }} ``` ## Higher-Order Components ```typescript // withLoading.ts import { defineComponent, h, ref, onMounted } from 'vue'; export function withLoading(Component: any, loadFn: () => Promise) { return defineComponent({ setup(props, { attrs, slots }) { const loading = ref(true); const error = ref(null); onMounted(async () => { try { await loadFn(); } catch (e) { error.value = e as Error; } finally { loading.value = false; } }); return () => { if (loading.value) { return h('div', 'Loading...'); } if (error.value) { return h('div', `Error: ${error.value.message}`); } return h(Component, { ...props, ...attrs }, slots); }; } }); } // Usage const UserProfile = withLoading( UserProfileComponent, async () => { // Load user data } ); ``` ## When to Use This Skill Use vue-component-patterns when building modern, production-ready applications that require: - Reusable component libraries - Complex component communication - Type-safe component APIs - Flexible content projection with slots - Deep prop passing without prop drilling - Modal and portal management - Component performance optimization - Large-scale component architectures ## Component Design Best Practices 1. **Single Responsibility** - Each component should do one thing well 2. **Props down, events up** - Data flows down via props, changes flow up via events 3. **Use TypeScript** - Type-safe props and emits prevent bugs 4. **Validate props** - Use runtime validation for critical props 5. **Provide defaults** - Use `withDefaults` for optional props 6. **Use scoped slots** - Share component state with consumers 7. **Avoid prop drilling** - Use provide/inject for deep passing 8. **Use `v-model` for two-way binding** - Especially for form inputs 9. **Compose with slots** - Make components flexible and reusable 10. **Keep components small** - Extract complex logic to composables ## Component Anti-Patterns 1. **Mutating props** - Props are readonly, emit events instead 2. **Tight coupling** - Components shouldn't know about their parents 3. **Global state in components** - Use composables or stores instead 4. **Too many props** - Consider slots or composition 5. **Nested v-model** - Can cause confusion, be explicit 6. **Not using TypeScript** - Loses type safety and DX 7. **Overusing provide/inject** - Use for app-level state, not everything 8. **No prop validation** - Can lead to runtime errors 9. **Mixing concerns** - Separate UI, logic, and data fetching 10. **Not cleaning up** - Remove event listeners in `onUnmounted` ## Common Component Patterns ### Form Input Component ```typescript {{ label }} * {{ error }} ``` ### Data Table Component ```typescript {{ col.label }} {{ item[col.key] }} ``` ## Resources - [Vue 3 Component Documentation](https://vuejs.org/guide/essentials/component-basics.html) - [Props Documentation](https://vuejs.org/guide/components/props.html) - [Events Documentation](https://vuejs.org/guide/components/events.html) - [Slots Documentation](https://vuejs.org/guide/components/slots.html) - [Provide/Inject Documentation](https://vuejs.org/guide/components/provide-inject.html) - [TypeScript with Vue](https://vuejs.org/guide/typescript/composition-api.html)
Count: {{ count }}
Card content goes here
Mouse position: {{ x }}, {{ y }}
Count: {{ state.count }}
This is teleported to body!