--- title: Component Slots Best Practices impact: MEDIUM impactDescription: Poor slot API design causes empty DOM wrappers, weak TypeScript safety, brittle defaults, and unnecessary component overhead type: best-practice tags: [vue3, slots, components, typescript, composables] --- # Component Slots Best Practices **Impact: MEDIUM** - Slots are a core component API surface in Vue. Structure them intentionally so templates stay predictable, typed, and performant. ## Task List - Use shorthand syntax for named slots (`#` instead of `v-slot:`) - Render optional slot wrapper elements only when slot content exists (`$slots` checks) - Type scoped slot contracts with `defineSlots` in TypeScript components - Provide fallback content for optional slots - Prefer composables over renderless components for pure logic reuse ## Shorthand syntax for named slots **BAD:** ```vue ``` **GOOD:** ```vue ``` ## Conditionally Render Optional Slot Wrappers Use `$slots` checks when wrapper elements add spacing, borders, or layout constraints. **BAD:** ```vue ``` **GOOD:** ```vue ``` ## Type Scoped Slot Props with defineSlots In ` ``` **GOOD:** ```vue ``` ## Provide Slot Fallback Content Fallback content makes components resilient when parents omit optional slots. **BAD:** ```vue ``` **GOOD:** ```vue ``` ## Prefer Composables for Pure Logic Reuse Renderless components are still useful for slot-driven composition, but composables are usually cleaner for logic-only reuse. **BAD:** ```vue ``` **GOOD:** ```ts // composables/useMouse.ts import { ref, onMounted, onUnmounted } from 'vue' export function useMouse() { const x = ref(0) const y = ref(0) function onMove(event: MouseEvent) { x.value = event.pageX y.value = event.pageY } onMounted(() => window.addEventListener('mousemove', onMove)) onUnmounted(() => window.removeEventListener('mousemove', onMove)) return { x, y } } ``` ```vue ```