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.