Props and defineProps
Props are how parent components pass data down to child components. In FamilyTree, PersonCard receives a Person object as a prop from FocusView — the parent supplies the data, and PersonCard displays it.
Declaring props with defineProps
Section titled “Declaring props with defineProps”defineProps is a compiler macro available inside <script setup>. The TypeScript-generic form is the cleanest:
<script setup lang="ts">defineProps<{ name: string birthYear?: number role?: 'focus' | 'parent' | 'child' | 'spouse'}>()</script>
<template> <div class="person-card" :class="role"> <span class="name">{{ name }}</span> <span v-if="birthYear" class="year">b. {{ birthYear }}</span> </div></template>The props declared in defineProps are automatically available in the template — no need to reference props.name, just name.
Passing props from a parent
Section titled “Passing props from a parent”In the parent template, pass props as HTML attributes:
<script setup lang="ts">import PersonCard from '../components/PersonCard.vue'</script>
<template> <PersonCard name="Alice" :birth-year="1982" role="focus" /> <PersonCard name="Bob" role="parent" /></template>Static string props can be passed as plain strings: name="Alice". Dynamic props — values from variables — need the : binding: :birth-year="person.birthYear".
Note the naming convention: birthYear in the props definition becomes :birth-year in the template (camelCase → kebab-case). Vue handles this automatically.
Prop defaults with withDefaults
Section titled “Prop defaults with withDefaults”When you need default values, wrap defineProps with withDefaults:
<script setup lang="ts">withDefaults(defineProps<{ name: string role?: 'focus' | 'parent' | 'child' | 'spouse'}>(), { role: 'focus',})</script>If the parent doesn’t pass role, the component uses 'focus' as the default.
One-way data flow
Section titled “One-way data flow”Props flow downward — parent to child. A child component must never mutate its props directly. This is Vue’s one-way data flow principle.
If a child needs to change something, it emits an event (covered in the next lesson) and the parent updates its own state in response.
<!-- Wrong — don't do this --><script setup lang="ts">const props = defineProps<{ count: number }>()props.count++ // ❌ mutating a prop</script>If you need a local copy of a prop to modify, copy it into a ref:
<script setup lang="ts">import { ref } from 'vue'const props = defineProps<{ initialCount: number }>()const count = ref(props.initialCount) // ✅ local copy</script>Exercise
Section titled “Exercise”- Update
PersonCard.vueto acceptname: string,birthYear?: number, androle?: stringas props. - Display the name and conditionally show the birth year.
- In
App.vue, render three<PersonCard />instances with different prop values. - Add a prop
isDeceased?: booleanand display ”†” after the name if true.
defineProps<{ ... }>()declares typed props in<script setup>— props are available directly in the template.- Static string props use plain attributes; dynamic props use
:attr="value". withDefaultsprovides fallback values for optional props.- Props are read-only in the child — copy into a
refif you need a local mutable value. - Data flows one way: parent → child via props; child → parent via emits (next lesson).