Chapter 53: Vue Built-in Elements
Vue Built-in Elements (also called built-in special tags or reserved template elements).
These are very special HTML-like tags that Vue treats in a magical way. They are not normal HTML elements — they don’t exist in the browser’s DOM specification — but Vue understands them perfectly and compiles them into powerful behavior.
When you write <Transition>, <KeepAlive>, <Teleport>, <component>, <slot>, <Suspense>, etc. in a Vue template, you’re using Vue’s built-in elements.
They are always available — no import needed, no registration — and they solve very common UI and architecture problems in a clean, declarative way.
In Vue 3 (2026 standard), there are 7 main built-in elements that real developers actually use every week. Here they are, in the order of how often you’ll reach for them in practice:
1. <component :is=”…”> — Dynamic / Runtime Component (Most Used)
This is the king of built-in elements — used in almost every medium/large app.
It lets you render any component (or even native HTML tag) dynamically — the tag name is decided by a variable at runtime.
Realistic example – Tabbed dashboard
|
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 |
<template> <div class="dashboard"> <div class="tabs"> <button v-for="tab in tabs" :key="tab.name" :class="{ active: activeTab === tab.name }" @click="activeTab = tab.name" > {{ tab.label }} </button> </div> <!-- ★ The magic built-in element ★ --> <component :is="currentTabComponent" :key="activeTab" /> </div> </template> <script setup lang="ts"> import { ref, computed, defineAsyncComponent } from 'vue' const Overview = () => import('@/components/tabs/Overview.vue') const Analytics = defineAsyncComponent(() => import('@/components/tabs/Analytics.vue')) const Settings = () => import('@/components/tabs/Settings.vue') const tabs = [ { name: 'overview', label: 'Overview', component: Overview }, { name: 'analytics', label: 'Analytics', component: Analytics }, { name: 'settings', label: 'Settings', component: Settings } ] const activeTab = ref('overview') const currentTabComponent = computed(() => { return tabs.find(t => t.name === activeTab.value)?.component || null }) </script> |
→ Switch tabs → completely different component renders at the same spot → :is = the component object (imported or async)
2. <Transition> — Single-element enter/leave animation
Wraps one element that appears/disappears.
|
0 1 2 3 4 5 6 7 8 9 10 |
<Transition name="fade"> <div v-if="showModal" class="modal"> Modal content </div> </Transition> |
|
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
.fade-enter-active, .fade-leave-active { transition: opacity 0.4s ease; } .fade-enter-from, .fade-leave-to { opacity: 0; } |
→ Modal fades in/out smoothly — no JavaScript animation code needed
3. <TransitionGroup> — List (v-for) animation
Special version for lists — animates enter, leave, and move/reorder.
|
0 1 2 3 4 5 6 7 8 9 10 11 12 13 |
<TransitionGroup name="list" tag="ul"> <li v-for="todo in todos" :key="todo.id" > {{ todo.text }} </li> </TransitionGroup> |
|
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
.list-enter-active, .list-leave-active, .list-move { transition: all 0.4s ease; } .list-enter-from, .list-leave-to { opacity: 0; transform: translateX(-30px); } .list-leave-active { position: absolute; width: 100%; } |
→ Add/remove items → fade + slide → Reorder/sort/filter → items slide to new positions (.move class)
4. <KeepAlive> — Cache & preserve component state
Keeps components alive in memory when hidden (tabs, router-view).
|
0 1 2 3 4 5 6 7 8 |
<KeepAlive> <component :is="currentTab" /> </KeepAlive> |
or around router-view:
|
0 1 2 3 4 5 6 7 8 9 10 |
<router-view v-slot="{ Component }"> <KeepAlive> <component :is="Component" /> </KeepAlive> </router-view> |
→ Switch tabs → form inputs, scroll position, internal state stay exactly as left
5. <Teleport> — Move DOM subtree to another location
Renders children inside another part of the DOM (usually <body>).
|
0 1 2 3 4 5 6 7 8 9 10 |
<Teleport to="body"> <div v-if="showModal" class="modal"> Modal content — appears at end of body </div> </Teleport> |
→ Modal is logically inside your component (props, events, styles) → Physically appended to <body> → appears above everything (z-index, overflow)
6. <Suspense> — Handle async loading states
Shows fallback while child is loading (async setup / lazy components).
|
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
<Suspense> <template #default> <AsyncHeavyDashboard /> </template> <template #fallback> <div class="loading">Loading dashboard...</div> </template> </Suspense> |
→ Beautiful loading states for lazy routes / heavy components
7. <slot> — (technically not a component, but built-in)
Defines content insertion points — already covered in previous lessons.
|
0 1 2 3 4 5 6 7 8 |
<slot name="header" /> <slot /> <!-- default slot --> <slot name="footer" /> |
Quick Reference Table – Vue Built-in Components (2026)
| Built-in Element | Primary Purpose | Most Common Use Case | Frequency in Real Apps |
|---|---|---|---|
| <component :is> | Dynamic component at runtime | Tabs, role-based UI, wizards, CMS blocks | ★★★★★ (daily) |
| <Transition> | Single enter/leave animation | Modals, dropdowns, alerts, tab content | ★★★★★ |
| <TransitionGroup> | List enter/leave/move animation | Todo lists, cards, search results, messages | ★★★★☆ |
| <KeepAlive> | Cache component state when hidden | Tabs, router-view, dynamic components | ★★★★☆ |
| <Teleport> | Move content to different DOM location | Modals, toasts, popovers, overlays | ★★★★☆ |
| <Suspense> | Handle async loading states | Lazy components, async setup | ★★★☆☆ (growing fast) |
| <slot> | Define content insertion points | Reusable layouts, cards, modals | ★★★★★ |
Pro Tips from Real Projects (Hyderabad 2026)
- Always add :key when using dynamic :is — prevents state leakage
- Combine <Transition> + <KeepAlive> for animated cached tabs
- Use <Teleport to=”#modals”> + central #modals div in index.html
- <Suspense> + defineAsyncComponent = beautiful lazy-loading DX
- Test animations on mobile — 60Hz vs 120Hz feels very different
- Respect prefers-reduced-motion media query for accessibility
Your mini practice task:
- Build a tabbed interface using <component :is> + <KeepAlive>
- Add <Transition> fade when switching tabs
- Wrap a modal in <Teleport to=”body”>
- Add <TransitionGroup> to a todo list inside one tab
Any part confusing? Want full examples for:
- Animated router-view transitions?
- Real modal system with Teleport + Transition?
- <Suspense> + async setup example?
- Combining all of them in one dashboard page?
Just tell me — we’ll build the next polished, animated feature together 🚀
