Vue.js Component Props
Props are the mechanism for passing data from a parent component down to a child component. They make components flexible and reusable by letting the parent control what the child displays or uses.
How Props Work
Think of a component as a coffee vending machine. The machine is the same every time (the component), but you press a button to choose what goes in (the prop). The machine produces different results depending on what you selected.
Diagram: Props Flow
Parent Component
┌────────────────────────────────────────┐
│ <user-card │
│ name="Elena" │
│ role="Designer" │
│ :score="92" │
│ /> │
└──────────────────┬─────────────────────┘
│ Props flow DOWN
▼
Child Component (user-card)
┌────────────────────────────────────────┐
│ props: ["name", "role", "score"] │
│ │
│ Receives: │
│ name = "Elena" │
│ role = "Designer" │
│ score = 92 │
│ │
│ Renders its template using these │
└────────────────────────────────────────┘
Defining Props — Array Syntax
The simplest way to declare props is with an array of prop names.
app.component("user-card", {
props: ["name", "role", "score"],
template: `
<div>
<h3>{{ name }}</h3>
<p>Role: {{ role }}</p>
<p>Score: {{ score }}</p>
</div>
`
});
<user-card name="Elena" role="Designer" :score="92"></user-card>
Notice the difference: name="Elena" passes a string literal. :score="92" uses v-bind (the colon shorthand) to pass a number. Without the colon, score="92" would pass the string "92" instead of the number 92.
Defining Props — Object Syntax with Validation
The object syntax lets you define the expected type, whether the prop is required, and a default value. Vue checks these rules in development mode and warns you if something is wrong.
app.component("product-badge", {
props: {
label: {
type: String,
required: true
},
count: {
type: Number,
default: 0
},
active: {
type: Boolean,
default: true
}
},
template: `<span v-if="active">{{ label }}: {{ count }}</span>`
});
Supported Prop Types
┌─────────────┬──────────────────────────────────┐
│ Type │ Example value │
├─────────────┼──────────────────────────────────┤
│ String │ "Hello" │
│ Number │ 42 │
│ Boolean │ true / false │
│ Array │ ["a", "b", "c"] │
│ Object │ { id: 1, name: "Alice" } │
│ Function │ () => doSomething() │
│ Date │ new Date() │
└─────────────┴──────────────────────────────────┘
Props with Default Values
When a prop has a default, the component works even if the parent does not pass that prop.
app.component("alert-banner", {
props: {
message: {
type: String,
required: true
},
type: {
type: String,
default: "info" // used if parent doesn't pass "type"
},
dismissible: {
type: Boolean,
default: true
}
},
template: `
<div :class="'banner-' + type">
{{ message }}
<button v-if="dismissible">✕</button>
</div>
`
});
<!-- Only passes message; type and dismissible use defaults --> <alert-banner message="Your file was saved."></alert-banner> <!-- Overrides defaults --> <alert-banner message="Error occurred." type="error" :dismissible="false"> </alert-banner>
Passing Dynamic Props from Parent Data
Instead of hardcoding values in the template, bind props to the parent's data. When the parent data changes, the child updates automatically.
<div id="app">
<user-card
:name="currentUser.name"
:role="currentUser.role"
:score="currentUser.score"
></user-card>
</div>
<script>
const app = Vue.createApp({
data() {
return {
currentUser: {
name: "Marcus",
role: "Engineer",
score: 88
}
};
}
});
app.component("user-card", {
props: ["name", "role", "score"],
template: `
<div>
<h3>{{ name }}</h3>
<p>{{ role }} — Score: {{ score }}</p>
</div>
`
});
app.mount("#app");
</script>
Diagram: Dynamic Props Update Chain
Parent data: currentUser.name = "Marcus"
│
│ :name="currentUser.name"
▼
Child receives: name = "Marcus"
│
▼
Child renders: <h3>Marcus</h3>
Parent updates: currentUser.name = "Lisa"
│
│ Vue re-passes the prop automatically
▼
Child receives: name = "Lisa"
│
▼
Child re-renders: <h3>Lisa</h3>
Passing an Object as Props with v-bind
When a component has many props and the parent has a matching object, you can spread the object's properties as props using v-bind without an argument.
data() {
return {
product: {
name: "Standing Desk",
price: 399,
inStock: true
}
};
}
<!-- Pass each property individually -->
<product-card :name="product.name" :price="product.price" :inStock="product.inStock">
</product-card>
<!-- Shorthand: spread the whole object -->
<product-card v-bind="product"></product-card>
<!-- Both produce the same result -->
Props Are Read-Only in the Child
A child component must never directly modify a prop it receives. Props are a one-way data channel — down from parent to child only. Modifying a prop directly causes a Vue warning and leads to unpredictable behavior.
Diagram: Why Props Should Not Be Modified
Parent owns the data: user.name = "Tom"
│
│ passes prop: name = "Tom"
▼
Child receives: name = "Tom"
If child modifies name directly:
name = "Jerry" ← Vue warns: "Avoid mutating a prop"
Why it's wrong:
Parent still has user.name = "Tom"
Child shows "Jerry"
They are now out of sync — unpredictable behavior.
The correct approach:
Child emits an event → Parent updates user.name → Parent passes new prop
The Correct Pattern — Local Copy
If you need to change a prop's value inside the child, copy it into a local data property first.
app.component("editable-name", {
props: ["initialName"],
data() {
return {
localName: this.initialName // copy prop into local data
};
},
template: `
<div>
<input v-model="localName">
<p>Editing: {{ localName }}</p>
</div>
`
});
localName starts with the prop's value but is fully owned by the child. Changing it does not affect the parent.
Prop Validation with Custom Rules
For advanced validation, provide a validator function that returns true or false.
props: {
status: {
type: String,
validator(value) {
return ["active", "inactive", "pending"].includes(value);
}
},
age: {
type: Number,
validator(value) {
return value >= 0 && value <= 120;
}
}
}
Diagram: Validator in Action
<user-badge status="active"></user-badge> ✓ Valid <user-badge status="deleted"></user-badge> ✗ Vue warns: invalid prop value <age-field :age="25"></age-field> ✓ Valid <age-field :age="-5"></age-field> ✗ Vue warns: invalid prop value
Summary
Props pass data from parent components down to child components. Define them as an array for quick setup or as an object to add type checking, required flags, defaults, and custom validators. Use v-bind (the colon shorthand) to pass numbers, booleans, arrays, and objects — without it, everything becomes a string. Never modify props inside the child; copy them to local data if you need a changeable version. Props are the foundation of component communication, and the validation system helps catch misuse early during development.
