Loading source
Pulling the file list, source metadata, and syntax-aware rendering for this listing.
Source from repo
Vue 3 debugging reference for reactivity issues, computed errors, watcher bugs, async failures, and SSR hydration problems.
Files
Skill
Size
Entrypoint
Format
Open file
Syntax-highlighted preview of this file as included in the skill package.
reference/defineEmits-must-be-top-level.md
1---2title: defineEmits Must Be Used at Top Level of script setup3impact: HIGH4impactDescription: Using defineEmits inside functions causes compilation errors - macros must be at module scope5type: gotcha6tags: [vue3, defineEmits, script-setup, macros, composition-api]7---89# defineEmits Must Be Used at Top Level of script setup1011**Impact: HIGH** - The `defineEmits()` macro can only be used directly within `<script setup>` at the top level. It cannot be placed inside functions, conditionals, or any other nested scope. Vue's compiler hoists these macros to module scope during compilation.1213This applies to all Vue macros: `defineProps`, `defineEmits`, `defineExpose`, `defineOptions`, and `defineSlots`.1415## Task Checklist1617- [ ] Place `defineEmits()` directly in `<script setup>`, not inside functions18- [ ] Do not wrap macro calls in conditionals or loops19- [ ] Do not reference local variables in macro arguments20- [ ] Store the emit function and reuse it throughout the component2122## The Problem2324**Incorrect - Inside a function:**25```vue26<script setup>27function useEvents() {28// ERROR: defineEmits cannot be used inside a function29const emit = defineEmits(['submit', 'cancel'])30return emit31}3233const emit = useEvents() // This fails at compile time34</script>35```3637**Incorrect - Inside a conditional:**38```vue39<script setup>40if (someCondition) {41// ERROR: Cannot use defineEmits in conditional42const emit = defineEmits(['eventA'])43} else {44const emit = defineEmits(['eventB'])45}46</script>47```4849**Incorrect - Referencing local variables:**50```vue51<script setup>52const eventNames = ['submit', 'cancel']5354// ERROR: Cannot reference local variables55const emit = defineEmits(eventNames)56</script>57```5859## Correct Usage6061**Correct - Top level declaration:**62```vue63<script setup>64// CORRECT: defineEmits at top level of script setup65const emit = defineEmits(['submit', 'cancel', 'update'])6667function handleSubmit() {68emit('submit', data)69}7071function handleCancel() {72emit('cancel')73}74</script>75```7677**Correct - With TypeScript types:**78```vue79<script setup lang="ts">80// CORRECT: Type-based declaration at top level81const emit = defineEmits<{82submit: [data: FormData]83cancel: []84'update:modelValue': [value: string]85}>()8687function handleSubmit(data: FormData) {88emit('submit', data)89}90</script>91```9293**Correct - Using constant arrays (compile-time constant):**94```vue95<script setup>96// CORRECT: Literal array is fine97const emit = defineEmits(['submit', 'cancel'])98</script>99```100101## Why This Restriction Exists102103Vue's compiler processes `<script setup>` macros at compile time, not runtime. The arguments must be statically analyzable so Vue can:1041051. Generate the correct component options1062. Provide TypeScript type inference1073. Enable IDE support for event autocompletion1084. Validate emitted events109110Since the macro is hoisted out of `<script setup>` during compilation, it cannot access anything that only exists at runtime.111112## Using emit in Composables113114If you want to share emit logic in a composable, pass the emit function as an argument:115116**Correct - Pass emit to composable:**117```vue118<script setup>119const emit = defineEmits(['submit', 'cancel', 'validate'])120121// Pass emit to composable122const { handleSubmit, handleCancel } = useFormEvents(emit)123</script>124```125126```js127// composables/useFormEvents.js128export function useFormEvents(emit) {129function handleSubmit(data) {130emit('submit', data)131}132133function handleCancel() {134emit('cancel')135}136137return { handleSubmit, handleCancel }138}139```140141## ESLint Rule142143The `eslint-plugin-vue` provides the `vue/valid-define-emits` rule that catches these errors:144145```js146// eslint.config.js147export default [148{149rules: {150'vue/valid-define-emits': 'error'151}152}153]154```155156This rule reports:157- `defineEmits` used inside functions158- `defineEmits` referencing local variables159- Multiple `defineEmits` calls in the same component160- `defineEmits` used outside `<script setup>`161162## Reference163- [Vue.js SFC script setup](https://vuejs.org/api/sfc-script-setup.html#defineprops-defineemits)164- [ESLint vue/valid-define-emits](https://eslint.vuejs.org/rules/valid-define-emits)165