Props
Pass data from a parent component into a child using props, with validation and default values.
Declaring Props
A component declares the props it accepts using the defineProps compiler macro, available automatically inside <script setup> with no import needed.
<!-- ProductCard.vue --><script setup>const props = defineProps(['title', 'price']);</script>
<template> <div class="card"> <h3>{{ title }}</h3> <p>{{ price }}</p> </div></template>Passing Props from a Parent
A parent passes a prop exactly like an HTML attribute — as a static string, or dynamically bound with :prop to a JavaScript expression.
<script setup>import ProductCard from './ProductCard.vue';const product = { title: 'Wireless Mouse', price: '$24.99' };</script>
<template> <ProductCard title="Static Title" price="$0.00" /> <ProductCard :title="product.title" :price="product.price" /></template>Prop Types and Validation
For anything beyond a quick prototype, declare props with types, required flags, and default values — Vue validates these in development and warns clearly when something doesn't match.
<script setup>const props = defineProps({ title: { type: String, required: true }, price: { type: Number, required: true }, inStock: { type: Boolean, default: true }, tags: { type: Array, default: () => [] },});</script>default: [] would share the same array instance across every use of the component — always use default: () => [] (or () => ({})) so each instance gets its own copy.
One-Way Data Flow
Props flow only downward — from parent to child. A child must never mutate a prop directly; doing so triggers a warning, since the parent is the true owner of that data. If a child needs to change something derived from a prop, it should hold its own local reactive copy, or emit an event asking the parent to make the change.
Mutating a Prop Directly
const props = defineProps(['count']);
function increment() { props.count++; // ⚠️ Vue warns: never mutate a prop}Using Local State Instead
const props = defineProps(['count']);const localCount = ref(props.count);
function increment() { localCount.value++; // fine — this is the child\'s own state}Common Beginner Mistakes
This breaks one-way data flow and produces a runtime warning — emit an event instead, letting the parent decide whether and how to update its own data.
A prop named maxCount is declared in camelCase in <script>, but passed as max-count in templates (both work due to Vue's automatic conversion, but consistency matters for readability).
FAQs
Yes — a validator field alongside type lets you write custom validation logic, e.g. checking that a value is one of a specific set of allowed strings.
Vue logs a console warning in development, but the app still runs — the prop's value will simply be undefined unless a default is also provided.
Summary
Props pass data one way, from parent to child, and should be treated as read-only inside the child. Next, you'll learn the other direction: how a child communicates back up to its parent with custom events.