Props
Passing data into a component with defineProps, validating it, and why props flow only one direction.
読了時間 2 分
Props are how a parent component passes data down into a child. They're a component's public API — the things it declares it needs in order to render.
<!-- ProductCard.vue -->
<script setup>
defineProps(["name", "price"]);
</script>
<template>
<div class="card">
<h3>{{ name }}</h3>
<p>${{ price }}</p>
</div>
</template><!-- App.vue -->
<template>
<ProductCard name="Keyboard" :price="60" />
</template>Note :price uses v-bind shorthand while name doesn't. Without the colon, price would be passed as the literal string "60", not the number 60 — the colon is what tells Vue to evaluate the attribute's value as a JavaScript expression rather than treating it as a plain string.
Declaring props with validation
The array form (defineProps(["name", "price"])) works but gives you no type checking. The object form is more explicit and catches mistakes early, especially useful once a component has several props or other developers are consuming it:
<script setup>
const props = defineProps({
name: { type: String, required: true },
price: { type: Number, required: true },
inStock: { type: Boolean, default: true },
});
</script>
<template>
<div class="card">
<h3>{{ name }}</h3>
<p>${{ price.toFixed(2) }}</p>
<p v-if="!inStock">Out of stock</p>
</div>
</template>If a required prop is missing, or a passed value doesn't match its declared type, Vue logs a console warning in development — invaluable for catching integration mistakes (a typo'd prop name, a string passed where a number was expected) before they cause a harder-to-diagnose bug further downstream.
defineProps returns an object you can assign to a variable (props above) when you need to reference a prop from your <script setup> logic, not just from the template.
Props flow one direction: down
A prop is meant to be read-only from the child's perspective. Vue will warn you if you try to mutate a prop directly:
<script setup>
const props = defineProps(["count"]);
function increment() {
props.count++; // Warning: mutating a prop directly
}
</script>This is deliberate, not a limitation to work around. If a child could freely mutate a prop it doesn't own, the parent's own copy of that state and the child's rendered view of it can silently drift out of sync, and tracing where a value changed becomes a search through every component that received it — instead of always being the one place that declared it as its own reactive state.
If a component genuinely needs to modify a value it was given, the correct pattern is to keep the source of truth in the parent and have the child ask the parent to change it — by emitting an event, which is exactly what the next lesson covers.