Chapter 27: Local Components
What are “Local Components”?
In Vue, local components (also called locally registered components) are components that are only available inside one specific parent component.
They are not globally registered — which means:
- You cannot use them anywhere else in your app unless you import and register them again
- They are private to the file where they are declared
This is the opposite of global components (which you register once in main.ts with app.component(‘MyButton’, MyButton) and can use everywhere).
In modern Vue 3 (especially with <script setup>), almost all components are local by default — and that’s actually a good thing for most projects.
Why Local Components Are the 2026 Default (Big Advantages)
- No naming pollution You can have Button.vue in many different folders with completely different styles/logic — no conflict
- Better tree-shaking & bundle size Vite only includes components you actually import
- Clearer mental model When you see <MySpecialCard /> — you know exactly where to find its code (just look at imports)
- Easier refactoring Move/rename a component → only change one import path
- Encapsulation & predictability No surprise “why is this component working here but not there?”
How to Use Local Components (Modern <script setup> Way)
You just import the component and use it in the template — that’s it. No components: {} object needed anymore.
Real Example – Dashboard with Local Sub-Components
Imagine you’re building a user dashboard page.
File: src/views/DashboardView.vue (the page)
|
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 |
<template> <div class="dashboard"> <h1>Welcome, {{ userName }}</h1> <!-- Local components – only used here --> <StatsCard title="Total Orders" :value="stats.orders" color="blue" /> <StatsCard title="Revenue" :value="`₹${stats.revenue.toLocaleString()}`" color="green" /> <StatsCard title="New Users" :value="stats.newUsers" color="purple" /> <RecentActivity :activities="recentActivities" /> </div> </template> <script setup lang="ts"> import { ref } from 'vue' // Local – only imported & used in this file import StatsCard from '@/components/dashboard/StatsCard.vue' import RecentActivity from '@/components/dashboard/RecentActivity.vue' const userName = ref('Rahul') const stats = ref({ orders: 142, revenue: 285000, newUsers: 38 }) const recentActivities = ref([ { time: '10 min ago', text: 'New order #3948 from Priya' }, { time: '1 hr ago', text: 'User Rahul updated profile' }, { time: '2 hrs ago', text: 'Payment of ₹4,999 received' } ]) </script> <style scoped> .dashboard { padding: 2rem; max-width: 1200px; margin: 0 auto; } </style> |
Child 1: StatsCard.vue (local helper – only makes sense in dashboard context)
|
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 |
<!-- src/components/dashboard/StatsCard.vue --> <template> <div class="stats-card" :class="`color-{color}`"> <h3>{{ title }}</h3> <p class="value">{{ value }}</p> </div> </template> <script setup lang="ts"> defineProps<{ title: string value: string | number color: 'blue' | 'green' | 'purple' }>() </script> <style scoped> .stats-card { padding: 1.5rem; border-radius: 12px; background: white; box-shadow: 0 4px 15px rgba(0,0,0,0.06); text-align: center; min-width: 220px; } .value { font-size: 2.2rem; font-weight: bold; margin: 0.5rem 0 0; } .color-blue { border-top: 4px solid #3b82f6; } .color-green { border-top: 4px solid #22c55e; } .color-purple { border-top: 4px solid #a855f7; } </style> |
Child 2: RecentActivity.vue (also local)
|
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 |
<!-- src/components/dashboard/RecentActivity.vue --> <template> <div class="activity-box"> <h3>Recent Activity</h3> <ul> <li v-for="act in activities" :key="act.time"> <span class="time">{{ act.time }}</span> {{ act.text }} </li> </ul> </div> </template> <script setup lang="ts"> defineProps<{ activities: { time: string; text: string }[] }>() </script> <style scoped> .activity-box { margin-top: 2rem; padding: 1.5rem; background: white; border-radius: 12px; box-shadow: 0 4px 15px rgba(0,0,0,0.06); } ul { list-style: none; padding: 0; margin: 1rem 0 0; } li { padding: 0.8rem 0; border-bottom: 1px solid #e5e7eb; } .time { color: #6b7280; font-size: 0.9rem; margin-right: 1rem; } </style> |
Folder Structure Recommendation (2026 Pattern)
|
0 1 2 3 4 5 6 7 8 9 10 11 12 13 |
src/ ├── components/ │ ├── ui/ ← global reusable pieces (Button, Modal, Input…) │ └── dashboard/ ← local to DashboardView only │ ├── StatsCard.vue │ └── RecentActivity.vue ├── views/ │ └── DashboardView.vue ← imports local children |
→ This is very common in medium/large apps → Local components live next to the page that needs them
Global vs Local – Quick Decision Table
| Situation | Use Global Registration? | Use Local (import)? | Why? |
|---|---|---|---|
| Button, Card, Modal, FormInput | Yes (in main.ts or plugin) | No | Used on 20+ pages |
| StatsCard, RecentActivity (dashboard-specific) | No | Yes | Only used in one place |
| Page header, sidebar, footer | Yes | No | Repeated across app |
| One-off complex widget (only on /profile) | No | Yes | No need to pollute global scope |
Summary – Local Components in 2026
- Default & recommended in modern Vue 3 projects
- Just import → use in template
- No components: {} registration needed
- Keep them close to the page/feature that uses them (components/dashboard/, components/profile/, etc.)
- Makes code easier to find, refactor, and reason about
- Scales beautifully — no global namespace pollution
Your mini homework:
- Create a ProfileHeader.vue local component inside views/ProfileView.vue
- Move StatsCard from global to local (only used in Dashboard)
- Check bundle size before/after (spoiler: local = better tree-shaking)
Any part confusing? Want to see:
- How to make a component global later if needed?
- Local vs global performance impact?
- Folder-by-feature vs folder-by-type debate?
- Real example with nested local components?
Just tell me — we’ll build the next piece together 🚀
Happy local-component-ing from Hyderabad! 💙
