Skip to main content
Vue.js for Beginners to Advanced
CHAPTER 06 Beginner

Vue Components

Updated: May 18, 2026
5 min read

# CHAPTER 6

Vue Components

1. Chapter Introduction

Components are the core of Vue's architecture. A Vue application is a tree of components — each is a self-contained, reusable unit with its own template, logic, and styles. Building UI as components makes code modular, testable, and maintainable.

2. Learning Objectives

  • Understand the Single File Component (SFC) structure.
  • Create and use child components.
  • Register components globally and locally.
  • Pass data between parent and child.
  • Build a user dashboard with multiple components.

3. Single File Component Structure

vue
12345678910111213141516171819202122232425
<!-- UserCard.vue — A complete component in one .vue file -->

<!-- 1. SCRIPT: Component logic (Composition API) -->
<script setup>
import { ref, computed } from &#039;vue'

const user = ref({ name: &#039;Alice', role: 'Admin', score: 95 })
const badge = computed(() => user.value.score > 90 ? &#039;🏆 Expert' : '⭐ Regular')
</script>

<!-- 2. TEMPLATE: Component HTML -->
<template>
  <div class="user-card">
    <h3>{{ user.name }}</h3>
    <span class="role">{{ user.role }}</span>
    <span class="badge">{{ badge }}</span>
  </div>
</template>

<!-- 3. STYLE: Component CSS (scoped = private to this component) -->
<style scoped>
.user-card { border: 1px solid #e2e8f0; border-radius: 12px; padding: 1.5rem; }
.role { background: #ede9fe; color: #7c3aed; padding: .2rem .6rem; border-radius: 99px; font-size: .8rem; }
.badge { margin-left: .5rem; }
</style>

4. Using Components

vue
123456789101112131415161718
<!-- App.vue or any parent component -->
<script setup>
// Local registration — just import the .vue file
import UserCard from &#039;./components/UserCard.vue'
import NavBar from &#039;./components/NavBar.vue'
import Footer from &#039;./components/Footer.vue'
</script>

<template>
  <!-- Use imported components as custom HTML tags -->
  <NavBar />
  <main>
    <!-- PascalCase or kebab-case both work -->
    <UserCard />
    <user-card />  <!-- Same as <UserCard /> -->
  </main>
  <Footer />
</template>

5. Global Component Registration

main.js
1234567891011121314
import { createApp } from &#039;vue&#039;
import App from &#039;./App.vue&#039;
import BaseButton from &#039;./components/BaseButton.vue&#039;
import BaseCard from &#039;./components/BaseCard.vue&#039;
import BaseInput from &#039;./components/BaseInput.vue&#039;

const app = createApp(App)

// Global registration — available in ALL templates
app.component(&#039;BaseButton&#039;, BaseButton)
app.component(&#039;BaseCard&#039;, BaseCard)
app.component(&#039;BaseInput&#039;, BaseInput)

app.mount(&#039;#app&#039;)
text
1234
Naming Convention:
- PascalCase for component files: UserCard.vue, NavBar.vue
- Global base components: Base prefix — BaseButton, BaseCard, BaseInput
- Page components: *View suffix — HomeView.vue, ProfileView.vue

6. Component with Slots

vue
1234567891011121314151617181920212223242526272829
<!-- BaseCard.vue — Reusable card with slot -->
<template>
  <div class="base-card">
    <div class="card-header" v-if="$slots.header">
      <slot name="header" />
    </div>
    <div class="card-body">
      <slot />  <!-- Default slot -->
    </div>
    <div class="card-footer" v-if="$slots.footer">
      <slot name="footer" />
    </div>
  </div>
</template>

<!-- Usage in parent: -->
<BaseCard>
  <template #header>
    <h3>Card Title</h3>
  </template>

  <!-- Default slot content -->
  <p>Main card content goes here.</p>
  <button>Action</button>

  <template #footer>
    <span class="text-muted">Last updated: today</span>
  </template>
</BaseCard>

7. Mini Project: User Dashboard UI

vue
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364
<!-- StatCard.vue -->
<script setup>
defineProps({
  title: String,
  value: [String, Number],
  icon: String,
  trend: Number,
  color: { type: String, default: &#039;blue' }
})
</script>
<template>
  <div class="stat-card" :class="&#039;card-' + color">
    <div class="icon">{{ icon }}</div>
    <div class="info">
      <div class="value">{{ value }}</div>
      <div class="title">{{ title }}</div>
      <div class="trend" :class="trend >= 0 ? &#039;up' : 'down'">
        {{ trend >= 0 ? &#039;↑' : '↓' }} {{ Math.abs(trend) }}%
      </div>
    </div>
  </div>
</template>
<style scoped>
.stat-card { display: flex; align-items: center; gap: 1rem; padding: 1.25rem; border-radius: 12px; background: white; box-shadow: 0 2px 8px rgba(0,0,0,.08); }
.icon { font-size: 2.5rem; }
.value { font-size: 1.75rem; font-weight: 800; }
.title { color: #64748b; font-size: .875rem; }
.up { color: #22c55e; font-size: .8rem; font-weight: 600; }
.down { color: #ef4444; font-size: .8rem; font-weight: 600; }
</style>

<!-- Dashboard.vue (uses StatCard) -->
<script setup>
import StatCard from &#039;./StatCard.vue'
import { ref } from &#039;vue'

const stats = ref([
  { title: &#039;Total Revenue', value: '$24,500', icon: '💰', trend: 12.5, color: 'blue' },
  { title: &#039;Active Users', value: '1,847', icon: '👥', trend: 8.3, color: 'green' },
  { title: &#039;Orders', value: '342', icon: '📦', trend: -3.1, color: 'orange' },
  { title: &#039;Growth', value: '24.8%', icon: '📈', trend: 4.1, color: 'purple' },
])
</script>
<template>
  <div class="dashboard">
    <h1>Dashboard Overview</h1>
    <div class="stats-grid">
      <StatCard
        v-for="stat in stats"
        :key="stat.title"
        :title="stat.title"
        :value="stat.value"
        :icon="stat.icon"
        :trend="stat.trend"
        :color="stat.color"
      />
    </div>
  </div>
</template>
<style scoped>
.dashboard { padding: 2rem; background: #f8fafc; min-height: 100vh; font-family: sans-serif; }
h1 { color: #1e293b; margin-bottom: 1.5rem; }
.stats-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(240px, 1fr)); gap: 1rem; }
</style>

8. Common Mistakes

  • Not using <style scoped>: Without scoped, your component's CSS will affect ALL components globally, causing style conflicts.
  • Circular imports: ComponentA imports ComponentB which imports ComponentA. Break the cycle by restructuring.

9. MCQs

Question 1

SFC stands for?

Question 2

What are the three sections of a .vue file?

Question 3

What does <style scoped> do?

Question 4

How do you use a component in another component?

Question 5

Global component registration is done in?

Question 6

Naming convention for component files?

Question 7

What is a slot in Vue?

Question 8

Default slot syntax?

Question 9

Named slot usage in parent?

Question 10

$slots.header is truthy when?

10. Interview Questions

  • Q: What is a Single File Component? What are its advantages?
  • Q: When would you use global vs local component registration?

11. Summary

Vue's component system with SFCs encapsulates template, logic, and styles in .vue files. Local imports are preferred for treeshaking. Global registration is appropriate for frequently-used base components. Slots enable powerful composition patterns — parent components can inject content into child component templates.

12. Next Chapter Recommendation

In Chapter 7: Props and Component Communication, we master passing data from parent to child (props) and from child to parent (emit events).

Finish this Chapter

Save your progress on your learning path and prepare for coding interview challenges.

Discussion

Join the discussion

Log in or create a free account to participate.

Sort: ·