Vue.js Components Basics

A component is a self-contained, reusable piece of a webpage. Instead of building your entire page in one block, you split it into focused pieces — a navigation bar, a product card, a comment box. Each piece is a component. You build it once and use it as many times as needed.

Why Components Exist

Imagine building a city without prefabricated parts. Every building would need to be constructed from raw materials, one brick at a time. Components are like prefabricated rooms — you design a room type once (bedroom, bathroom, kitchen) and insert as many copies as needed.

Diagram: Page Built from Components

┌──────────────────────────────────────┐
│           Full Web Page              │
│                                      │
│  ┌────────────────────────────────┐  │
│  │      <NavBar /> component      │  │
│  └────────────────────────────────┘  │
│                                      │
│  ┌──────────┐  ┌──────────┐          │
│  │ <Card /> │  │ <Card /> │          │
│  └──────────┘  └──────────┘          │
│  ┌──────────┐  ┌──────────┐          │
│  │ <Card /> │  │ <Card /> │          │
│  └──────────┘  └──────────┘          │
│                                      │
│  ┌────────────────────────────────┐  │
│  │      <Footer /> component      │  │
│  └────────────────────────────────┘  │
└──────────────────────────────────────┘

Creating a Global Component

You register a component using app.component() before mounting the app. The first argument is the component name and the second is the component object.

<div id="app">
  <greeting-card></greeting-card>
  <greeting-card></greeting-card>
  <greeting-card></greeting-card>
</div>

<script>
  const app = Vue.createApp({});

  app.component("greeting-card", {
    template: `
      <div>
        <h3>Hello from a component!</h3>
        <p>This card is reusable.</p>
      </div>
    `
  });

  app.mount("#app");
</script>

Diagram: Component Used Multiple Times

Template:
  <greeting-card></greeting-card>   ← instance 1
  <greeting-card></greeting-card>   ← instance 2
  <greeting-card></greeting-card>   ← instance 3

Each instance renders its own copy:
  ┌─────────────────────┐  ┌─────────────────────┐  ┌─────────────────────┐
  │ Hello from a        │  │ Hello from a        │  │ Hello from a        │
  │ component!          │  │ component!          │  │ component!          │
  │ This card is        │  │ This card is        │  │ This card is        │
  │ reusable.           │  │ reusable.           │  │ reusable.           │
  └─────────────────────┘  └─────────────────────┘  └─────────────────────┘

Component Names: PascalCase vs kebab-case

Vue components can be named in PascalCase (GreetingCard) or kebab-case (greeting-card). When registering, you can use either. In HTML templates, always use the kebab-case form because HTML is not case-sensitive.

// Register as PascalCase
app.component("GreetingCard", { ... });

// Use in HTML as kebab-case
<greeting-card></greeting-card>

// Also valid in HTML (Vue converts automatically)
<GreetingCard />

Components Have Their Own Data

Each component instance has its own separate data. Changing data in one instance does not affect the others. This is why data() must be a function in components — it returns a fresh data object for each instance.

<div id="app">
  <counter-button></counter-button>
  <counter-button></counter-button>
  <counter-button></counter-button>
</div>

<script>
  const app = Vue.createApp({});

  app.component("counter-button", {
    data() {
      return { count: 0 };
    },
    template: `
      <button @click="count++">
        Clicked {{ count }} time(s)
      </button>
    `
  });

  app.mount("#app");
</script>

Diagram: Independent Instance Data

Three counter-button instances:

Instance 1: count = 3  →  "Clicked 3 time(s)"
Instance 2: count = 1  →  "Clicked 1 time(s)"
Instance 3: count = 0  →  "Clicked 0 time(s)"

Clicking Instance 1 does NOT affect Instance 2 or 3.
Each has its own private count.

Component Template Rules

A component's template must have a single root element in Vue 2. In Vue 3, multiple root elements are allowed.

// Vue 3: multiple roots allowed
app.component("my-component", {
  template: `
    <h3>Title</h3>
    <p>Description text.</p>
  `
});

Local vs Global Components

A global component (registered with app.component()) is available everywhere in your app. A local component is registered inside another component's options and is only available within that parent.

Local Component Registration

const StatusBadge = {
  template: `<span>Active</span>`
};

const app = Vue.createApp({
  components: {
    StatusBadge   // Only available in this component
  },
  template: `
    <div>
      <status-badge></status-badge>
    </div>
  `
});

Diagram: Global vs Local Scope

Global component (app.component):
┌─────────────────────────────────────┐
│             Entire App              │
│  ┌─────────┐  ┌─────────┐           │
│  │Component│  │Component│           │
│  │    A    │  │    B    │  ← Both can use <nav-bar>
│  └─────────┘  └─────────┘           │
└─────────────────────────────────────┘

Local component (registered inside Component A):
┌─────────────────────────────────────┐
│  Component A                        │
│  ┌──────────────────────────────┐   │
│  │  <status-badge> ← available  │   │
│  └──────────────────────────────┘   │
│                                     │
│  Component B → <status-badge> NOT available
└─────────────────────────────────────┘

Passing Data into Components with Props

Components can receive data from their parent through props. This makes a component flexible — instead of displaying the same content every time, it displays whatever the parent sends.

app.component("product-card", {
  props: ["name", "price"],
  template: `
    <div>
      <h4>{{ name }}</h4>
      <p>Price: ${{ price }}</p>
    </div>
  `
});
<product-card name="Desk Lamp" price="35"></product-card>
<product-card name="USB Hub"   price="22"></product-card>

Diagram: Props Flow from Parent to Component

Parent template:
  <product-card name="Desk Lamp" price="35"></product-card>
                     │                │
                     ▼                ▼
            component receives props
            name = "Desk Lamp"
            price = "35"
                     │
                     ▼
            template renders:
            ┌─────────────────┐
            │  Desk Lamp      │
            │  Price: $35     │
            └─────────────────┘

Component Communication Overview

Diagram: Component Relationship Directions

Parent
  │
  │  Props (data flows DOWN)
  ▼
Child Component
  │
  │  Events (signals flow UP)
  ▼
Parent receives the event

Data flows down through props. Signals flow up through emitted events. This one-directional flow keeps components predictable and easy to debug.

A Practical Component: Alert Box

<div id="app">
  <alert-box message="Your session will expire in 5 minutes."></alert-box>
  <alert-box message="New update available. Please refresh."></alert-box>
</div>

<script>
  const app = Vue.createApp({});

  app.component("alert-box", {
    props: ["message"],
    data() {
      return { visible: true };
    },
    template: `
      <div v-if="visible">
        <strong>⚠ Alert:</strong> {{ message }}
        <button @click="visible = false">Dismiss</button>
      </div>
    `
  });

  app.mount("#app");
</script>

Diagram: Independent Dismiss Behavior

Alert 1: "Your session will expire..."   [Dismiss]
Alert 2: "New update available..."       [Dismiss]

User clicks Dismiss on Alert 1:
  Alert 1: visible = false → removed from screen
  Alert 2: visible = true  → still shown (unaffected)

Summary

Components split your Vue application into focused, reusable pieces. Register them globally with app.component() or locally inside a component's components option. Each instance has its own independent data thanks to the data() function pattern. Pass data into components with props. Components communicate back to their parent through events. This structure keeps your code organized, reusable, and easy to maintain as your application grows.

Leave a Comment

Your email address will not be published. Required fields are marked *