Vue.js: Component Basics
Last updated: 2026-10-20
A Vue component is a reusable Vue instance — every component encapsulates its own template, logic, and styles. The core architecture of Vue 3 is the "component tree": root component (App.vue) → page components → business components → base components (Button/Input).
Mastering component basics is a key step toward writing large Vue applications. This lesson helps you understand what a component really is, how to register one, the naming conventions, and why SFCs (Single-File Components) are the recommended way to write Vue.
1. What You'll Learn
- What a component is, and how components relate to Vue instances
- The 3 parts of a Single-File Component (template / script / style)
- Global registration vs local registration: differences and when to use each
- Component naming conventions (PascalCase / kebab-case)
- Component reuse (avoiding duplicated code)
- The basic concepts of props and emit (covered in detail in the next two lessons)
- 5 component design principles
2. The Code Duplication Disaster of a 30-Page E-commerce Admin
(1) Pain Point: 30 Product Cards, Copy-Pasted 30 Times
Alice's e-commerce admin needs to show 30 product cards across several pages. Her first approach:
<!-- ❌ Broken version: 30 product cards copied 30 times -->
<!-- In HomeView.vue -->
<div class="product-card">
<img src="iphone.jpg">
<h3>iPhone 15 Pro</h3>
<p>$999</p>
<button>Add to Cart</button>
</div>
<div class="product-card">
<img src="macbook.jpg">
<h3>MacBook Pro</h3>
<p>$2499</p>
<button>Add to Cart</button>
</div>
<!-- ... and 28 more ... -->
<!-- The same code shows up again in ProductList.vue, SearchResults.vue, Cart.vue -->
Product manager Charlie added 5 new requests:
"Alice, I want the button text changed from 'Add to Cart' to 'Add to Bag'. Also the card should show stock status. And it needs to be responsive on mobile."
Alice had to edit 30+ HTML files × 3 rounds = 90 edits. That is simply unmaintainable.
(2) The Vue Component Solution: 1 ProductCard.vue, 30 Reuses
1 ProductCard.vue → 30 reuses. Changing the button text means changing 1 place.
(3) Benefits
After componentizing:
- Lines of code: 30 × 15 lines × 3 pages = 1350 lines → 1 component + 30 lines × 3 = 90 lines (-93%)
- Change 1 place: all 30 product cards update automatically
- Add a feature: add it to the component and every page gets it
- Testability: ProductCard.vue can be tested on its own
3. What Is a Component?
(1) Definition
A component is a reusable UI unit with its own logic and styles in Vue. Each component = 1 Vue instance + its own data/methods/lifecycle.
graph TB
A[App.vue<br/>Root component] --> B[HomeView.vue<br/>Home page]
A --> C[AboutView.vue<br/>About]
A --> D[DashboardView.vue<br/>Dashboard]
B --> E[ProductCard.vue<br/>Product card]
B --> F[ProductList.vue<br/>Product list]
B --> G[FilterBar.vue<br/>Filter bar]
E --> H[Button.vue<br/>Button]
E --> I[Badge.vue<br/>Badge]
F --> E
G --> J[Select.vue<br/>Select]
G --> K[Input.vue<br/>Input]
style A fill:#42b883,color:#fff
style E fill:#42b883,color:#fff
style H fill:#42b883,color:#fff
(2) 5 Core Characteristics
| Characteristic | Description |
|---|---|
| Reusable | 1 component can be used in N places |
| Encapsulated | Its own data / methods / style, independent of others |
| Composable | Small components compose into large ones |
| Testable | Unit-test a single component |
| Maintainable | Change 1 place = change N places |
(3) Components vs Functions
| Dimension | Function | Component |
|---|---|---|
| Unit of reuse | Code logic | UI + logic |
| Input | Arguments | props |
| Output | return | emit |
| State | Local variables | data / refs |
| Side effects | None | Lifecycle hooks |
▶ Example: Reusing ProductCard on 3 Pages
▶ Try it YourselfOutput:
Three sections, each rendering the same ProductCard component with different data: Home Featured lists iPhone and MacBook, Product List All lists all three products, and Category Phones lists only the iPhone.
Every row prints the name plus a line such as $999 | Stock: 50, and no card markup is duplicated in the source.
Home Featured
iPhone
$999 | Stock: 50
MacBook
$2499 | Stock: 20
Product List All
iPhone
$999 | Stock: 50
MacBook
$2499 | Stock: 20
iPad
$599 | Stock: 30
Category Phones
iPhone
$999 | Stock: 50
4. Single-File Components (SFC)
(1) What Is an SFC?
SFC (Single File Component) = 1 .vue file = 1 component. It keeps template, script, and style in a single file, which makes them easier to manage.
(2) The 3 Parts of an SFC
| Part | Purpose | Required? | Language |
|---|---|---|---|
<template> |
HTML template | ✅ | HTML + Vue directives |
<script setup> |
JS logic | ✅ | JavaScript / TypeScript |
<style scoped> |
CSS styles | ❌ | CSS / SCSS / Less |
(3) SFC vs HTML Strings
// ❌ Without an SFC (string template, Vue 2 style)
Vue.component('my-component', {
template: '<div>{{ msg }}</div>',
data() { return { msg: 'Hello' } }
})
// ✅ With an SFC (recommended)
// MyComponent.vue
<template>
<div>{{ msg }}</div>
</template>
<script setup>
const msg = 'Hello'
</script>
SFC advantages:
- Editor support (highlighting, go-to-definition, autocomplete)
- Tooling support (Vite HMR, TypeScript type checking)
- Separation of concerns (one block each for HTML/JS/CSS)
5. Component Registration
(1) Global Registration (Not Recommended for Large Projects)
// main.js
const { createApp } = Vue
import App from './App.vue'
import ProductCard from './components/ProductCard.vue'
const app = createApp(App)
// Global registration: every component can use <ProductCard />
app.component('ProductCard', ProductCard)
app.mount('#app')
(2) Local Registration (Recommended)
<!-- HomeView.vue -->
<script setup>
// 1. Import
import ProductCard from '@/components/ProductCard.vue'
// 2. Use it in the template (no registration needed, it is available automatically)
</script>
<template>
<ProductCard :product="product" />
</template>
(3) Comparing the Two
| Dimension | Global registration | Local registration |
|---|---|---|
| Scope | The whole application | The current component |
| Code location | main.js |
Inside the component's <script setup> |
| Tree-shaking | ❌ Not supported | ✅ Supported |
| Type hints | Weak | Strong (IDE autocomplete) |
| Recommendation | ⭐⭐ For plugins | ⭐⭐⭐⭐⭐ For business code |
▶ Example: Global Registration vs Local Registration
▶ Try it YourselfOutput:
A single blue button labeled Locally Registered Button.
Mounting replaces the content of #app, so the three <my-button> tags written in the raw HTML body never render — only the template of the locally registered App component does.
Uncommenting app.component('MyButton', MyButton) would make the component available application-wide instead.
[Locally Registered Button] (blue #3b82f6, white text)
6. Component Naming Conventions
(1) 3 Naming Styles
| Style | Example | Usage | Recommendation |
|---|---|---|---|
| PascalCase | ProductCard.vue |
JS imports / in templates | ⭐⭐⭐⭐⭐ |
| kebab-case | product-card.vue |
File names / kebab-case templates | ⭐⭐⭐ |
| camelCase | productCard.vue |
Not recommended | ⭐ |
(2) Using Them in Templates
(3) Folder Structure
src/
├── components/ # Shared components (used across pages)
│ ├── ProductCard.vue
│ ├── Button.vue
│ └── Header.vue
├── views/ # Page components (used by the router)
│ ├── HomeView.vue
│ ├── ProductListView.vue
│ └── DashboardView.vue
├── composables/ # Composables
│ ├── useAuth.ts
│ └── useFetch.ts
├── router/ # Router configuration
└── App.vue # Root component
7. props and emit Basics
(1) props: Parent → Child
(2) emit: Child → Parent
(3) props vs emit Cheat Sheet
| Direction | API | Purpose |
|---|---|---|
| Parent → Child | defineProps() |
Pass data |
| Child → Parent | defineEmits() |
Notify with events |
▶ Example: props 5 Types + Validators
▶ Try it YourselfOutput:
One bordered card with a heading Administrator - Alice and three lines: Age: 25 | Status: Online, Role: admin | Frameworks: Vue, React, Angular, Email: alice@example.com | Per page: 20.
All nine props pass validation, so the console stays silent;
a value like status: 'blocked' or an email without @ would trigger a Vue prop validation warning.
Administrator - Alice
Age: 25 | Status: Online
Role: admin | Frameworks: Vue, React, Angular
Email: alice@example.com | Per page: 20
(1) 📊 5 Component Design Principles
| Principle | Description |
|---|---|
| Single responsibility | 1 component does 1 thing |
| Reusable | Abstract common UI (Button/Input/Modal) |
| Configurable | props control behavior, slots control content |
| Composable | Small components compose into large ones |
| Easy to test | Independent components are easy to unit-test |
(2) 📊 5 Common Mistakes
| Mistake | Symptom | Fix |
|---|---|---|
| Single-word component names | IDE error | Use PascalCase: ProductCard.vue |
| Too much global registration | Bigger bundle | Local registration supports tree-shaking |
| Components that are too large | Hard to maintain | Split into small components |
| Mutating props directly | Vue warning | props are read-only, use emit |
| Forgetting to import a component | Template shows "unknown component" | Check the imports in <script setup> |
8. Putting It Together: The ProductCard Component End to End
▶ Example: ProductCard Component (Multi-Component CDN Demo)
▶ Expand runnable example (65 lines)
Output:
A Featured Products heading followed by three cards.
The first two show Only 5 left (stock 5) and an active Add to Cart button;
the third card is dimmed by .out-of-stock and its button reads Out of Stock with disabled.
The footer line starts at Cart items: 0 and increases by one each time an enabled button is clicked.
Featured Products
iPhone 15 Pro
$999
[Add to Cart]
MacBook Pro
$2499
Only 5 left
[Add to Cart]
Sold Out Item
$99
[Out of Stock] (disabled, card dimmed)
Cart items: 0
❓ FAQ
<el-button>); (2) base components used by the whole app (Button/Icon). Business components should always be registered locally so tree-shaking can work.<template> and <script> are required, <style> is optional. If a component contains only JS (for example it just wraps data), it can skip the template and use a render function.@vue/test-utils): mount(ProductCard, { props: { product: mockProduct } }). Phase 4.6 of this tutorial covers it in detail.<el-button> directly in templates. See Phase 4.7 for details.📖 Summary
- A component is a reusable Vue instance that encapsulates template / script / style
- SFC (.vue files) is the recommended way to write Vue 3, with good editor and tooling support
- Local registration > global registration (tree-shaking friendly)
- Naming: PascalCase (ProductCard) > kebab-case (product-card)
- props pass data down (defineProps), emit sends events up (defineEmits)
- Component design principles: single responsibility, reusable, configurable, composable, easy to test
- Folder structure: components/ for shared components, views/ for page components, composables/ for composables
📝 Exercises
-
Basic (difficulty ⭐) Write a Button component
src/components/BaseButton.vue:- props:
text(String),variant(String, 'primary'/'success'/'danger') - 3 variants mapped to 3 classes
- emit a
clickevent - Use 3 different variants in HomeView.vue
- props:
-
Intermediate (difficulty ⭐⭐) Build a product list + card system:
- ProductCard.vue: a single product card (props: product, emit: add-to-cart)
- ProductList.vue: the list container (v-for + filtering)
- App.vue: the root component, passing data + handling add-to-cart
- 3 files, 3 components nested together
-
Challenge (difficulty ⭐⭐⭐) Build a complete "user card" component system:
- Avatar.vue: avatar component (supports both url / initials modes)
- UserCard.vue: user card (uses Avatar)
- UserList.vue: user list (uses UserCard)
- App.vue: root component (data + interactions)
- 5 props (avatar/name/email/role/active)
- 3 emitted events (edit/delete/select)
- scoped style isolation