Components are Vue's unit of reuse and organisation; props are how parents configure them.
Each .vue file is a component. To use one, import it in <script setup> and write it as a tag, conventionally in PascalCase: <TodoItem />. No registration step is needed.
A component declares the data it accepts from its parent with props, using the defineProps compiler macro (no import needed):
const props = defineProps({
title: { type: String, required: true },
count: { type: Number, default: 0 },
})With TypeScript you can write defineProps<{ title: string; count?: number }>() instead. Since Vue 3.5, destructuring props is reactive and is the idiomatic way to give defaults: const { title, count = 0 } = defineProps(...). The compiler rewrites count to props.count, so it stays reactive.
The parent passes props as attributes: static title="Inbox" or bound :count="unread" (without the colon, count="3" is the string "3"). Props declared in camelCase can be written kebab-case in templates (:max-items).
Props follow one-way data flow: when the parent's value changes the child updates, but the child must never assign to a prop. Vue warns if you try. If the child needs a local, editable copy, initialise a ref from the prop; if it wants to change the parent's data, it emits an event (next lesson).
Example
<!-- TodoItem.vue -->
<script setup>
const { text, done = false, priority = 1 } = defineProps({
text: { type: String, required: true },
done: Boolean,
priority: Number,
})
</script>
<template>
<li :class="{ done }">{{ text }} (p{{ priority }})</li>
</template>
<!-- TodoList.vue -->
<script setup>
import TodoItem from './TodoItem.vue'
const todos = [{ id: 1, text: 'Write props', done: true, priority: 2 }]
</script>
<template>
<ul>
<TodoItem v-for="t in todos" :key="t.id"
:text="t.text" :done="t.done" :priority="t.priority" />
<TodoItem text="Static text" /> <!-- done=false, priority=1 -->
</ul>
</template>Remember
- Import a component in
<script setup>and use it as a PascalCase tag definePropsdeclares inputs; destructure with defaults in Vue 3.5+- Data flows down one way: never assign to a prop
Watch out for
- Passing
count="3"without the colon, which sends the string "3" rather than a number.