Pulled ALL skills from 15 source repositories: - anthropics/skills: 16 (docs, design, MCP, testing) - obra/superpowers: 14 (TDD, debugging, agents, planning) - coreyhaines31/marketingskills: 25 (marketing, CRO, SEO, growth) - better-auth/skills: 5 (auth patterns) - vercel-labs/agent-skills: 5 (React, design, Vercel) - antfu/skills: 16 (Vue, Vite, Vitest, pnpm, Turborepo) - Plus 13 individual skills from various repos Mosaic Stack is not limited to coding — the Orchestrator and subagents serve coding, business, design, marketing, writing, logistics, analysis, and more. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
169 lines
4.2 KiB
Markdown
169 lines
4.2 KiB
Markdown
---
|
|
title: Teleport Preserves Logical Component Hierarchy
|
|
impact: LOW
|
|
impactDescription: Understanding that Teleport only changes DOM structure, not Vue's component tree
|
|
type: best-practice
|
|
tags: [vue3, teleport, component-hierarchy, props, events]
|
|
---
|
|
|
|
# Teleport Preserves Logical Component Hierarchy
|
|
|
|
**Impact: LOW** - A common misconception is that Teleport breaks the Vue component relationship. In reality, `<Teleport>` only alters the rendered DOM structure, not the logical hierarchy. Props, events, provide/inject, and Vue Devtools all work as if the component wasn't teleported.
|
|
|
|
## Task Checklist
|
|
|
|
- [ ] Continue using props and events normally with teleported components
|
|
- [ ] Use provide/inject across teleported boundaries
|
|
- [ ] Check Vue Devtools for component location (shows logical parent, not DOM location)
|
|
|
|
**Key Concept:**
|
|
```vue
|
|
<!-- ParentComponent.vue -->
|
|
<template>
|
|
<div class="parent">
|
|
<Teleport to="body">
|
|
<!-- ChildComponent is logically still a child of ParentComponent -->
|
|
<!-- even though it renders in <body> -->
|
|
<ChildComponent
|
|
:message="parentMessage"
|
|
@update="handleUpdate"
|
|
/>
|
|
</Teleport>
|
|
</div>
|
|
</template>
|
|
|
|
<script setup>
|
|
import { provide, ref } from 'vue'
|
|
|
|
const parentMessage = ref('Hello from parent')
|
|
|
|
// Provide still works across teleport
|
|
provide('theme', 'dark')
|
|
|
|
function handleUpdate(value) {
|
|
// Events bubble up through logical hierarchy, not DOM
|
|
console.log('Received from teleported child:', value)
|
|
}
|
|
</script>
|
|
```
|
|
|
|
```vue
|
|
<!-- ChildComponent.vue -->
|
|
<template>
|
|
<div class="modal">
|
|
<p>{{ message }}</p>
|
|
<p>Theme: {{ theme }}</p>
|
|
<button @click="$emit('update', 'new value')">Update</button>
|
|
</div>
|
|
</template>
|
|
|
|
<script setup>
|
|
import { inject } from 'vue'
|
|
|
|
// Props work normally
|
|
defineProps(['message'])
|
|
|
|
// Inject works across teleport boundary
|
|
const theme = inject('theme')
|
|
|
|
// Emit works normally
|
|
defineEmits(['update'])
|
|
</script>
|
|
```
|
|
|
|
## What Teleport Changes vs. Preserves
|
|
|
|
| Aspect | Changed? | Notes |
|
|
|--------|----------|-------|
|
|
| DOM position | Yes | Content moves to `to` target |
|
|
| CSS inheritance | Yes | Styles inherit from new DOM parent |
|
|
| Props | No | Work exactly as without Teleport |
|
|
| Events (emit) | No | Bubble through logical hierarchy |
|
|
| Provide/Inject | No | Work across teleport boundaries |
|
|
| Vue Devtools | No | Shows logical component tree |
|
|
| Slots | No | Work normally |
|
|
| Template refs | No | Parent can ref teleported content |
|
|
|
|
## Practical Example: Modal with Form
|
|
|
|
```vue
|
|
<!-- ModalForm.vue -->
|
|
<template>
|
|
<Teleport to="body">
|
|
<div v-if="visible" class="modal-overlay">
|
|
<form @submit.prevent="handleSubmit" class="modal-form">
|
|
<!-- Slot content receives parent's scope -->
|
|
<slot :formData="formData" />
|
|
|
|
<button type="submit">Submit</button>
|
|
<button type="button" @click="$emit('close')">Cancel</button>
|
|
</form>
|
|
</div>
|
|
</Teleport>
|
|
</template>
|
|
|
|
<script setup>
|
|
import { reactive } from 'vue'
|
|
|
|
defineProps(['visible'])
|
|
const emit = defineEmits(['close', 'submit'])
|
|
|
|
const formData = reactive({})
|
|
|
|
function handleSubmit() {
|
|
emit('submit', formData)
|
|
}
|
|
</script>
|
|
```
|
|
|
|
```vue
|
|
<!-- ParentPage.vue -->
|
|
<template>
|
|
<button @click="showModal = true">Add User</button>
|
|
|
|
<!-- Events and slots work as expected despite teleportation -->
|
|
<ModalForm
|
|
:visible="showModal"
|
|
@close="showModal = false"
|
|
@submit="handleFormSubmit"
|
|
>
|
|
<template #default="{ formData }">
|
|
<input v-model="formData.name" placeholder="Name" />
|
|
<input v-model="formData.email" placeholder="Email" />
|
|
</template>
|
|
</ModalForm>
|
|
</template>
|
|
|
|
<script setup>
|
|
import { ref } from 'vue'
|
|
|
|
const showModal = ref(false)
|
|
|
|
function handleFormSubmit(data) {
|
|
console.log('Form submitted:', data)
|
|
showModal.value = false
|
|
}
|
|
</script>
|
|
```
|
|
|
|
## Vue Devtools Behavior
|
|
|
|
In Vue Devtools, teleported components appear under their logical parent:
|
|
|
|
```
|
|
App
|
|
└── ParentPage
|
|
└── ModalForm <-- Shows here (logical parent)
|
|
└── form content
|
|
```
|
|
|
|
Not under their DOM location:
|
|
```
|
|
// This is NOT how it appears in Devtools
|
|
body
|
|
└── ModalForm <-- Does NOT show here
|
|
```
|
|
|
|
## Reference
|
|
- [Vue.js Teleport - Component Hierarchy](https://vuejs.org/guide/built-ins/teleport.html#basic-usage)
|