Vue Components, Props and Emits
Components let you split a Vue app into small, reusable pieces that each do one job.
Creating and Using a Component
Create a file src/components/CourseCard.vue:
Example
<script setup>
const title = "Web Development";
</script>
<template>
<div class="card">
<h3>{{ title }}</h3>
</div>
</template>
<style scoped>
.card {
border: 1px solid #ddd;
border-radius: 8px;
padding: 12px;
}
</style>To use it in App.vue, import it in <script setup> and write it as a tag:
Example
<script setup>
import CourseCard from "./components/CourseCard.vue";
</script>
<template>
<h1>Our Courses</h1>
<CourseCard />
<CourseCard />
</template>With <script setup>, an imported component is ready to use. No extra registration is needed.
Component names use PascalCase, like CourseCard, and are usually made of two or more words.
Props: Passing Data Down
Props let the parent send data into a child. Declare them with defineProps:
Example
<!-- CourseCard.vue -->
<script setup>
const props = defineProps({
title: String,
months: Number,
isNew: { type: Boolean, default: false },
});
</script>
<template>
<div class="card">
<h3>{{ title }} <span v-if="isNew">(New)</span></h3>
<p>Duration: {{ months }} months</p>
</div>
</template>The parent passes them like attributes. Use : for anything that is not a plain string:
Example
<CourseCard title="Web Development" :months="6" />
<CourseCard title="Artificial Intelligence" :months="4" :is-new="true" />Web Development Duration: 6 months Artificial Intelligence (New) Duration: 4 months
In the template you can write isNew or is-new; Vue matches both. In the script, read a prop as props.title.
Props are read-only in the child. If a child needs to change something, it should ask the parent with an event.
Emits: Sending Events Up
A child tells its parent that something happened by emitting an event. Declare events with defineEmits:
Example
<!-- EnrolButton.vue -->
<script setup>
const props = defineProps({ course: String });
const emit = defineEmits(["enrol"]);
</script>
<template>
<button @click="emit('enrol', props.course)">Enrol in {{ course }}</button>
</template>The parent listens with @:
Example
<script setup>
import { ref } from "vue";
import EnrolButton from "./components/EnrolButton.vue";
const chosen = ref("");
</script>
<template>
<EnrolButton course="Graphic Designing" @enrol="(name) => (chosen = name)" />
<p v-if="chosen">You chose {{ chosen }}.</p>
</template>(after click) You chose Graphic Designing.
| Direction | Tool |
|---|---|
| Parent to child | Props (defineProps) |
| Child to parent | Events (defineEmits) |
v-model on a Component
Vue 3.4 and later have defineModel, which makes v-model work on your own components:
Example
<!-- QuantityInput.vue -->
<script setup>
const quantity = defineModel({ type: Number, default: 1 });
</script>
<template>
<button @click="quantity--">-</button>
{{ quantity }}
<button @click="quantity++">+</button>
</template>Example
<QuantityInput v-model="plates" />Slots: Passing Content
A <slot> is a place where the parent can put its own content:
Example
<!-- InfoBox.vue -->
<template>
<div class="box">
<slot>Default text if nothing is given</slot>
</div>
</template>Example
<InfoBox>
<strong>Note:</strong> Classes are closed on Sunday.
</InfoBox>Note: Classes are closed on Sunday.
Rendering Components in a List
Example
<CourseCard
v-for="c in courses"
:key="c.id"
:title="c.name"
:months="c.months"
/>What Comes Next
When your app grows, you will want more tools:
- Vue Router for multiple pages, like
/coursesand/contact - Pinia to share state between many components
- Nuxt as a full framework with server rendering
Exercise
Exercise
How does a child component send information back to its parent in Vue?
