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.
