Chapter 91: Vue ‘props’ Option
The props option
This is the official, clean, safe way to pass data from parent to child component — the primary mechanism for parent → child communication in Vue.
Even in 2026 — when almost every new project uses <script setup> + defineProps() — you still need to understand the old props: { … } syntax because:
- You will read/maintain legacy Vue 2 / early Vue 3 code
- Many job interviews (especially in India) still ask about Options API
- Many older tutorials, Stack Overflow answers, and plugins still show it
- Vue Devtools still displays props in this format
So let’s go through it step by step — like I’m sitting next to you explaining both the old world and why we mostly moved to the new one.
1. What is the props option? (Very simple mental model)
props is an object (or array) that declares which data the parent is allowed to pass to this child component.
Vue takes that declaration and:
- makes those names available on the component instance (this.user, this.isAdmin)
- validates the types (in development mode)
- provides defaults
- separates them clearly from $attrs (unknown attributes go to $attrs)
|
0 1 2 3 4 5 6 7 8 9 10 |
props: { user: Object, isAdmin: Boolean, size: String } |
After Vue processes this:
- Parent can pass :user=”currentUser”
- Inside child → this.user is available (read-only)
- You never mutate props — child should emit events upward if it needs to change something
2. Real, Complete Example – UserProfileCard with Props
|
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 |
<template> <div class="user-card" :class="cardSizeClass"> <img :src="avatarUrl" alt="Avatar" class="avatar" /> <h2>{{ fullName }}</h2> <p class="email">{{ email }}</p> <div v-if="isAdmin" class="admin-badge"> Admin Privileges </div> <p v-if="showAge">Age: {{ age }}</p> <slot name="actions"></slot> </div> </template> <script> export default { name: 'UserProfileCard', // ── This is the props option ─────────────────────────────── props: { // Basic type declaration user: { type: Object, required: true, default: () => ({ name: 'Guest', email: '', avatar: '' }) }, // Boolean – common pattern isAdmin: { type: Boolean, default: false }, // String with validator size: { type: String, default: 'medium', validator(value) { return ['small', 'medium', 'large'].includes(value) } }, // Number with default showAge: { type: Boolean, default: true } }, computed: { fullName() { return this.user.name || 'Anonymous' }, email() { return this.user.email || 'No email provided' }, avatarUrl() { return this.user.avatar || 'https://via.placeholder.com/150?text=User' }, cardSizeClass() { return `size-${this.size}` } } } </script> <style scoped> .user-card { padding: 2rem; background: white; border-radius: 12px; box-shadow: 0 4px 15px rgba(0,0,0,0.08); max-width: 400px; margin: 2rem auto; text-align: center; } .avatar { width: 120px; height: 120px; border-radius: 50%; object-fit: cover; margin-bottom: 1rem; } .admin-badge { background: #dbeafe; color: #1e40af; padding: 0.4rem 1rem; border-radius: 9999px; display: inline-block; margin: 1rem 0; font-weight: bold; } .size-small { transform: scale(0.85); } .size-medium { transform: scale(1); } .size-large { transform: scale(1.15); } </style> |
Parent usage example
|
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
<UserProfileCard :user="currentUser" :is-admin="true" size="large" :show-age="false" > <template #actions> <button>Edit Profile</button> <button>Logout</button> </template> </UserProfileCard> <script> export default { data() { return { currentUser: { name: 'Rahul Sharma', email: 'rahul@example.com', avatar: 'https://example.com/avatar.jpg' } } } } </script> |
3. Important Rules & Gotchas (2026 Must-Know)
| Rule / Gotcha | Correct Behavior / Best Practice |
|---|---|
| props can be array or object | Array: props: [‘user’, ‘isAdmin’] — simple but no types/defaults Object: recommended |
| Props are read-only | Never do this.user.name = ‘new’ → Vue warns / throws in dev mode |
| Props are reactive | Parent changes :user → child auto-updates |
| Optional props | No required: true → can be undefined |
| Default values | default: false / default: () => ({}) (function for objects/arrays) |
| Type validation | type: String, type: Number, type: Object, type: Array, type: Boolean |
| Custom validator | validator(value) { return value > 0 } — throws warning in dev if false |
| Still used in 2026? | Yes — in legacy code, some plugins, interviews, but not in new <script setup> code |
4. Modern Vue 3 – Why We Almost Never Use props: { … } Anymore
In <script setup> (the 2026 standard):
|
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
<script setup lang="ts"> // Type-safe, autocompletion, required props, defaults — all in one line defineProps<{ user: { name: string email: string avatar?: string } isAdmin?: boolean size?: 'small' | 'medium' | 'large' showAge?: boolean }>() </script> |
→ No props object → No this → No separate defaults/validators (TypeScript does it) → Much cleaner & type-safe
Quick Summary Table – props in 2026
| Question | Options API (legacy) | Composition API (<script setup>) | What you should do in new code |
|---|---|---|---|
| How to declare props? | props: { user: Object, … } | defineProps<{ user: {…} }>() | Composition API |
| Access in template | {{ user.name }} | {{ user.name }} | — |
| Access in methods/computed | this.user.name | user.name | — |
| TypeScript friendly? | Poor (manual PropType) | Excellent (native types) | Use Composition |
| Still used in 2026? | Yes — legacy code, plugins, interviews | Almost never (except legacy) | Avoid unless maintaining old code |
Final 2026 Advice from Real Projects
- In new projects → never write props: { … } — use defineProps<…>() in <script setup>
- When you see props as an object → it means Options API (legacy style)
- Learn to read Options API — many jobs, open-source projects, tutorials still use it
- Never teach beginners props option as primary — start with defineProps in <script setup>
- If migrating old code → gradually convert props: { … } → defineProps<…>()
Your mini homework:
- Create the UserProfileCard component above in Options API
- Pass different user objects from parent → see props work
- Try mutating a prop inside child → see Vue warning in console
- Convert it to <script setup> → compare readability & TypeScript support
Any part confusing? Want to see:
- Full Options API vs Composition API props comparison project?
- How props looks in Vue Devtools?
- Common bugs when mutating props?
- Real component written in both styles?
Just tell me — we’ll convert and compare together step by step 🚀
