On this page
Components Basics
Components are the building blocks of Vue apps. Each component encapsulates template, logic, and styles in a single .vue file.
Creating a Component
components/UserCard.vue:
<script setup>
defineProps({
name: { type: String, required: true },
email: String,
role: { type: String, default: 'member' },
});
</script>
<template>
<article class="card">
<h3>{{ name }}</h3>
<p>{{ email }}</p>
<span class="badge">{{ role }}</span>
</article>
</template>
<style scoped>
.card { border: 1px solid #ddd; padding: 1rem; border-radius: 8px; }
</style>
Use it in a parent:
<script setup>
import UserCard from './components/UserCard.vue';
</script>
<template>
<UserCard name="Alice" email="[email protected]" role="admin" />
</template>
Props — Parent to Child
Props are read-only. Never mutate them directly:
<script setup>
const props = defineProps({
title: String,
count: { type: Number, default: 0 },
});
// Access via props.title in script
</script>
<template>
<h2>{{ title }} ({{ count }})</h2>
</template>
Emits — Child to Parent
Components communicate upward with events:
<!-- components/CounterButton.vue -->
<script setup>
const count = defineModel('count', { type: Number, default: 0 });
const emit = defineEmits(['increment', 'reset']);
function add() {
count.value++;
emit('increment', count.value);
}
</script>
<template>
<button @click="add">Count: {{ count }}</button>
<button @click="emit('reset')">Reset</button>
</template>
Parent listens with @:
<script setup>
import { ref } from 'vue';
import CounterButton from './components/CounterButton.vue';
const total = ref(0);
function handleIncrement(value) {
console.log('New count:', value);
}
</script>
<template>
<CounterButton v-model:count="total" @increment="handleIncrement" />
</template>
Slots — Content Projection
Let parents inject custom content into child components:
<!-- components/BaseCard.vue -->
<template>
<div class="card">
<header><slot name="header">Default Header</slot></header>
<main><slot /></main>
<footer><slot name="footer" /></footer>
</div>
</template>
<BaseCard>
<template #header>
<h2>User Profile</h2>
</template>
<p>Main content goes here.</p>
<template #footer>
<button>Save</button>
</template>
</BaseCard>
Keep components focused, use props down and emits up, and name components in PascalCase in templates.
Next: learn lifecycle hooks to run code at specific component stages.