Components and Props

Chapter 6
Components and Props
Passing data into a child component with defineProps — Vue's equivalent of React props and Angular @Input

Chapter 1's child components were static. Props make a component reusable by letting a parent pass data into it — the same concept as React props and Angular @Input. In <script setup>, props are declared with the compiler macro defineProps.

Declaring Props with defineProps

<!-- UserCard.vue --> <script setup> const props = defineProps(['name', 'age']); </script> <template> <h3>{{ name }}</h3> <p>Age: {{ age }}</p> </template>

defineProps(['name', 'age']) declares the props this component accepts. It's a compiler macro — no import needed; Vue's compiler recognizes it inside <script setup>. In the template, props are used directly by name ({{ name }}), just like a local ref. In the script, they're accessed via the returned object (props.name).

Passing Props from a Parent

<!-- parent template --> <script setup> import UserCard from './UserCard.vue'; </script> <template> <UserCard name="Philip" :age="35" /> <UserCard name="Sam" :age="28" /> </template>

Import the child, then place its tag with the props as attributes. The crucial detail, carried over from v-bind in Chapter 3: name="Philip" (no colon) passes the literal string "Philip", but :age="35" (with the colon) evaluates as an expression and passes the real number 35. Forgetting the colon on a number/boolean/array prop passes a string instead — the same string-vs-expression distinction as React's quotes-vs-curly-braces and Angular's plain-vs-bracket attributes.

Typed Props with Defaults and Validation

const props = defineProps({ name: { type: String, required: true }, age: { type: Number, default: 0 }, isAdmin: { type: Boolean, default: false }, });

The object form of defineProps adds type checking, a required flag, and default values — Vue warns in the console if a required prop is missing or the wrong type is passed. This is richer than React's plain props (which need PropTypes or TypeScript for the same), and comparable to Angular's typed @Input. Defaults work like Angular's, supplying a fallback when the parent omits the prop entirely.

Passing Different Data Types

<ProductCard title="Keyboard" <!-- string --> :price="49.99" <!-- number --> :in-stock="true" <!-- boolean --> :tags="['new', 'sale']" <!-- array --> />

Anything that isn't a plain string needs the : binding. Note also the naming convention: a prop declared as inStock (camelCase in JS) is passed as :in-stock (kebab-case in the template) — Vue maps between them automatically, the standard HTML-attribute convention. You can use camelCase in the template too, but kebab-case is the common style.

Props are read-only — same as everywhere
A child must never reassign a prop directly — Vue warns at runtime if you try. Props flow one way, parent to child, exactly as in React and Angular. If a child needs a local, editable copy, derive it (with a computed, or a ref initialized from the prop); to actually change the parent's data, emit an event — covered next chapter. Mutating an object/array prop's contents in place also works technically but is discouraged for the same reason: the parent loses control of its own data.
VueReactAngular
defineProps(['name'])Function params { name }@Input() name
name="x" (string)name="x"name="x"
:age="35" (real value)age={35}[age]="35"
{ type, required, default }PropTypes / TS + defaultsTyped input + default

Coding Challenges

Challenge 1

Build a MovieCard component with title and year props (declared via defineProps), rendering "Title (Year)". Use it three times in a parent with three different movies, passing year as a real number.

📄 View solution
Challenge 2

Build a Badge component using the object form of defineProps with a text prop (String, required) and a color prop (String, default "gray"). Render it once with a custom color and once without, confirming the default applies.

📄 View solution
Challenge 3

Build a ProductCard component receiving title (String), price (Number), and tags (Array) props, rendering the title, price, and the tags as a list. Pass tags as a real array using the : binding.

📄 View solution

Chapter 6 Quick Reference

  • defineProps([...]) — declares a component's props; a compiler macro, no import needed
  • Props used directly by name in the template; via props.name in the script
  • name="x" passes a string; :prop="expr" passes a real value (number/boolean/array/object)
  • Object form adds type, required, and default — richer than plain React props
  • camelCase prop ↔ kebab-case attribute (inStock:in-stock) mapped automatically
  • Props are read-only — flow one way; emit an event to change parent data (next chapter)
  • Next chapter: emitting events with defineEmits, and v-model on components