Skip to main content
Vue.js Essentials·Lesson 2 of 5

Components & Props

Vue applications are built from components — self-contained pieces of UI with their own logic and styling. Understanding how components communicate is the foundation of building maintainable Vue applications.

Creating a Component

Every Vue Single File Component (SFC) has three sections:

<script setup>
// Logic
import { ref } from 'vue'
const message = ref('Hello')
</script>

<template>
  <!-- Template -->
  <p>{{ message }}</p>
</template>

<style scoped>
/* Styles — scoped means they only apply to this component */
p { color: blue; }
</style>

Defining Props

Props are how parent components pass data to child components:

<!-- ChildComponent.vue -->
<script setup>
defineProps({
  title: String,
  count: {
    type: Number,
    default: 0
  },
  isActive: {
    type: Boolean,
    required: true
  }
})
</script>

<template>
  <div :class="{ active: isActive }">
    <h2>{{ title }}</h2>
    <p>Count: {{ count }}</p>
  </div>
</template>
<!-- ParentComponent.vue -->
<template>
  <ChildComponent title="My Title" :count="5" :is-active="true" />
</template>

Note: :count and :is-active use Vue's v-bind shorthand (:) to pass dynamic values. Without the colon, you pass a string literal.

Emitting Events

Child components communicate back to parents via events:

<!-- Button.vue -->
<script setup>
const emit = defineEmits(['click', 'hover'])

function handleClick() {
  emit('click', { timestamp: Date.now() })
}
</script>

<template>
  <button @click="handleClick">Click me</button>
</template>
<!-- Parent.vue -->
<script setup>
function onButtonClick(payload) {
  console.log('Clicked at:', payload.timestamp)
}
</script>

<template>
  <Button @click="onButtonClick" />
</template>

Slots

Slots allow parent components to inject content into child components:

<!-- Card.vue -->
<template>
  <div class="card">
    <slot name="header" />
    <div class="body">
      <slot />  <!-- default slot -->
    </div>
  </div>
</template>
<!-- Usage -->
<Card>
  <template #header>
    <h2>Card Title</h2>
  </template>
  <p>Card content goes here</p>
</Card>

Component Design Principles

Keep components small. A component that does one thing is easier to test and reuse than one that does many things.

Props down, events up. Data flows down via props; communication flows up via events. Don't mutate props directly in a child component.

Use TypeScript for props. With TypeScript, define props using interfaces for better type safety and IDE support:

interface Props {
  title: string
  count?: number
  isActive: boolean
}
const props = defineProps<Props>()