patternjavascriptvueModerate
Vue 3 defineProps and defineEmits in script setup
Viewed 0 times
Vue 3.0+ (script setup stable in 3.2)
definePropsdefineEmitsscript setupprops validationemitcomponent communication
Error Messages
Problem
Using defineProps and defineEmits incorrectly in <script setup> — forgetting to validate types, mutating props directly, or not using emit to communicate back to the parent.
Solution
Declare props and emits with runtime or TypeScript syntax:
<script setup lang="ts">
// TypeScript syntax (preferred)
const props = defineProps<{
title: string;
count?: number;
items: string[];
}>();
// With defaults using withDefaults
const props = withDefaults(defineProps<{
label?: string;
disabled?: boolean;
}>(), {
label: 'Click me',
disabled: false
});
// Runtime syntax (plain JS)
const props = defineProps({
title: { type: String, required: true },
count: { type: Number, default: 0 }
});
// Emits
const emit = defineEmits<{
(e: 'update:modelValue', value: string): void;
(e: 'submit', payload: { id: number }): void;
}>();
// Usage
function handleInput(val: string) {
// props.title = val; // WRONG: never mutate props
emit('update:modelValue', val); // CORRECT
}
</script>
<script setup lang="ts">
// TypeScript syntax (preferred)
const props = defineProps<{
title: string;
count?: number;
items: string[];
}>();
// With defaults using withDefaults
const props = withDefaults(defineProps<{
label?: string;
disabled?: boolean;
}>(), {
label: 'Click me',
disabled: false
});
// Runtime syntax (plain JS)
const props = defineProps({
title: { type: String, required: true },
count: { type: Number, default: 0 }
});
// Emits
const emit = defineEmits<{
(e: 'update:modelValue', value: string): void;
(e: 'submit', payload: { id: number }): void;
}>();
// Usage
function handleInput(val: string) {
// props.title = val; // WRONG: never mutate props
emit('update:modelValue', val); // CORRECT
}
</script>
Why
Props are a one-way data flow from parent to child. Mutating props directly creates hidden state bugs because the parent owns the data. Emits are the contract for child-to-parent communication. defineProps/defineEmits are compiler macros — they are not imported and are erased at compile time.
Gotchas
- defineProps and defineEmits are compiler macros — do NOT import them
- Never mutate props directly — emit events and let the parent update
- Props destructuring loses reactivity in Vue 3.4 and earlier — use props.x or toRefs(props)
- Vue 3.5+ allows destructuring props with reactivity via defineProps destructure RFC
Code Snippets
TypeScript defineProps with defaults
<script setup lang="ts">
const props = withDefaults(defineProps<{ size?: 'sm' | 'md' | 'lg' }>(), {
size: 'md'
});
const emit = defineEmits<{ (e: 'click'): void }>();
</script>Context
When building Vue 3 components with script setup syntax
Revisions (0)
No revisions yet.