Skip to main content
Vue.js for Beginners to Advanced
CHAPTER 28 Beginner

Advanced Vue.js Concepts

Updated: May 18, 2026
5 min read

# CHAPTER 28

Advanced Vue.js Concepts

1. Chapter Introduction

This chapter explores Vue 3's most powerful advanced features — tools that enterprise applications depend on for scalability, dynamic behavior, and architectural elegance.

2. Teleport

vue
1234567891011121314151617181920212223
<!-- Teleport renders content to a DIFFERENT DOM location -->
<!-- Great for modals, tooltips that need to escape overflow:hidden -->

<script setup>
import { ref } from &#039;vue'
const showModal = ref(false)
</script>

<template>
  <button @click="showModal = true">Open Modal</button>

  <!-- Rendered inside the current component tree in Vue -->
  <!-- But in the DOM, it appears as a child of <body> -->
  <Teleport to="body">
    <div v-if="showModal" class="modal-backdrop" @click.self="showModal = false">
      <div class="modal">
        <h2>Teleported Modal</h2>
        <p>This modal is rendered inside body, escaping any overflow:hidden.</p>
        <button @click="showModal = false">Close</button>
      </div>
    </div>
  </Teleport>
</template>

3. Suspense

vue
12345678910111213141516171819
<script setup>
import { defineAsyncComponent } from &#039;vue'
const AsyncDataTable = defineAsyncComponent(() => import(&#039;./DataTable.vue'))
</script>

<template>
  <Suspense>
    <!-- Shown when async component resolves -->
    <template #default>
      <AsyncDataTable />
    </template>
    <!-- Shown while async component loads -->
    <template #fallback>
      <div class="skeleton-loader">
        <div class="skeleton-row" v-for="n in 5" :key="n"></div>
      </div>
    </template>
  </Suspense>
</template>

4. provide / inject

javascript
12345678
// parent component or App.vue
import { provide, ref } from &#039;vue&#039;

const theme = ref(&#039;light&#039;)
const toggleTheme = () => { theme.value = theme.value === &#039;light&#039; ? &#039;dark&#039; : &#039;light&#039; }

// Provide to ALL descendants
provide(&#039;theme&#039;, { theme, toggleTheme })
javascript
123
// Any descendant component — no prop drilling!
import { inject } from &#039;vue&#039;
const { theme, toggleTheme } = inject(&#039;theme&#039;)

5. Dynamic Components

vue
1234567891011121314151617181920212223242526
<script setup>
import { ref, shallowRef } from &#039;vue'
import DashboardStats from &#039;./DashboardStats.vue'
import DashboardChart from &#039;./DashboardChart.vue'
import DashboardTable from &#039;./DashboardTable.vue'

const tabs = [
  { label: &#039;Stats', component: DashboardStats },
  { label: &#039;Chart', component: DashboardChart },
  { label: &#039;Table', component: DashboardTable }
]
const activeTab = shallowRef(DashboardStats)
</script>

<template>
  <div class="tabs">
    <button v-for="tab in tabs" :key="tab.label" @click="activeTab = tab.component">
      {{ tab.label }}
    </button>
  </div>

  <!-- <component :is="..."> renders dynamic component -->
  <KeepAlive>
    <component :is="activeTab" />
  </KeepAlive>
</template>

6. Custom Directives

javascript
123456789101112131415161718192021222324
// Global directive — registered in main.js
app.directive(&#039;tooltip&#039;, {
  mounted(el, binding) {
    el.setAttribute(&#039;title&#039;, binding.value)
    el._tooltip = tippy(el, { content: binding.value })
  },
  updated(el, binding) {
    el._tooltip.setContent(binding.value)
  },
  unmounted(el) {
    el._tooltip.destroy()
  }
})

// Local directive in <script setup>
const vClickOutside = {
  mounted(el, binding) {
    el._handler = (e) => !el.contains(e.target) && binding.value(e)
    document.addEventListener(&#039;click&#039;, el._handler)
  },
  unmounted(el) {
    document.removeEventListener(&#039;click&#039;, el._handler)
  }
}

7. Render Functions

javascript
1234567891011121314
// When templates aren't flexible enough
import { h, ref } from &#039;vue&#039;

// Functional component using render function
export default {
  props: { level: { type: Number, default: 1 } },
  setup(props, { slots }) {
    // Dynamic heading level: h1, h2, h3...
    return () => h(`h${props.level}`, { class: &#039;heading&#039; }, slots.default?.())
  }
}

// Usage: <DynamicHeading :level="2">My Title</DynamicHeading>
// Renders: <h2 class="heading">My Title</h2>

8. Plugin Development

MyPlugin.js
123456789101112131415161718192021222324252627282930313233
import MyToast from &#039;./MyToast.vue&#039;
import { createApp, ref } from &#039;vue&#039;

export const ToastPlugin = {
  install(app, options = {}) {
    const position = options.position || &#039;bottom-right&#039;

    // Add global property
    app.config.globalProperties.$toast = (message, type = &#039;info&#039;) => {
      const container = document.createElement(&#039;div&#039;)
      document.body.appendChild(container)
      const toastApp = createApp(MyToast, { message, type, position })
      toastApp.mount(container)
      setTimeout(() => { toastApp.unmount(); container.remove() }, 3000)
    }

    // Global component
    app.component(&#039;MyToast&#039;, MyToast)

    // Global directive
    app.directive(&#039;tooltip&#039;, { /* ... */ })

    // provide for inject
    app.provide(&#039;toastOptions&#039;, { position })
  }
}

// main.js
app.use(ToastPlugin, { position: &#039;top-right&#039; })

// In any component:
const { proxy } = getCurrentInstance()
proxy.$toast(&#039;Hello!&#039;, &#039;success&#039;)

9. Advanced Composables Patterns

javascript
1234567891011121314151617181920212223242526272829303132333435
// Composable with cleanup and lifecycle
export function useEventListener(target, event, handler) {
  onMounted(() => {
    (target.value ?? target).addEventListener(event, handler)
  })
  onUnmounted(() => {
    (target.value ?? target).removeEventListener(event, handler)
  })
}

// Composable returning computed async data
export function useAsyncData(fetcher) {
  const data = ref(null)
  const loading = ref(false)
  const error = ref(null)
  const controller = ref(null)

  async function execute(...args) {
    controller.value?.abort()
    controller.value = new AbortController()
    loading.value = true
    error.value = null
    try {
      data.value = await fetcher(...args, controller.value.signal)
    } catch (err) {
      if (err.name !== &#039;AbortError&#039;) error.value = err
    } finally {
      loading.value = false
    }
  }

  onUnmounted(() => controller.value?.abort())

  return { data, loading, error, execute }
}

10. MCQs

Question 1

<Teleport to="body"> renders content?

Question 2

<Suspense> #fallback shows when?

Question 3

provide/inject data flows?

Question 4

<component :is="..."> is used for?

Question 5

shallowRef for activeTab (component reference) prevents?

Question 6

Vue plugin install receives?

Question 7

Render function h() takes?

Question 8

defineAsyncComponent with timeout?

Question 9

Custom directive unmounted hook is for?

Question 10

app.config.globalProperties.$toast?

11. Interview Questions

  • Q: When would you use <Teleport> vs rendering a modal normally?
  • Q: What are the use cases for provide/inject vs Pinia?

12. Summary

Advanced Vue features enable enterprise-scale architecture. <Teleport> solves the modal z-index/overflow problem. <Suspense> handles async component loading declaratively. provide/inject eliminates prop drilling for DI patterns. Render functions enable ultimate flexibility when templates aren't enough.

13. Next Chapter Recommendation

In Chapter 29: Deploying Vue Applications, we build for production and deploy to Vercel, Netlify, and configure environment variables.

Finish this Chapter

Save your progress on your learning path and prepare for coding interview challenges.

Discussion

Join the discussion

Log in or create a free account to participate.

Sort: ·