]> git.ipfire.org Git - thirdparty/vuejs/core.git/commitdiff
chore: merge minor edison/feat/setScopeId 13422/head
authordaiwei <daiwei521@126.com>
Tue, 21 Oct 2025 05:52:00 +0000 (13:52 +0800)
committerdaiwei <daiwei521@126.com>
Tue, 21 Oct 2025 05:55:05 +0000 (13:55 +0800)
15 files changed:
1  2 
packages/compiler-vapor/src/generators/component.ts
packages/compiler-vapor/src/ir/index.ts
packages/compiler-vapor/src/transforms/transformElement.ts
packages/runtime-core/src/apiCreateApp.ts
packages/runtime-core/src/index.ts
packages/runtime-core/src/renderer.ts
packages/runtime-vapor/__tests__/componentSlots.spec.ts
packages/runtime-vapor/src/apiCreateApp.ts
packages/runtime-vapor/src/apiCreateDynamicComponent.ts
packages/runtime-vapor/src/apiDefineAsyncComponent.ts
packages/runtime-vapor/src/block.ts
packages/runtime-vapor/src/component.ts
packages/runtime-vapor/src/componentSlots.ts
packages/runtime-vapor/src/fragment.ts
packages/runtime-vapor/src/vdomInterop.ts

index c08de3808187ca24c2cbc4aea931d6e526842d6c,76ef7c53c49786ea1872aea731d6f5f36210a79d..894cb5d912562baa16d01833a20ba3b00ac24425
@@@ -196,7 -205,8 +205,9 @@@ export interface CreateComponentIRNode 
    dynamic?: SimpleExpressionNode
    parent?: number
    anchor?: number
 +  scopeId?: string | null
+   append?: boolean
+   last?: boolean
  }
  
  export interface DeclareOldRefIRNode extends BaseIRNode {
index 531496fb7433583694a7afceaa21e8320580df6e,caa39c4436b941af80803a81a70c3bffafdfbc37..b0712e8962bf72869e56ac905da34ce84378397b
@@@ -187,13 -187,27 +187,32 @@@ export interface VaporInteropInterface 
    unmount(vnode: VNode, doRemove?: boolean): void
    move(vnode: VNode, container: any, anchor: any): void
    slot(n1: VNode | null, n2: VNode, container: any, anchor: any): void
+   hydrate(
+     vnode: VNode,
+     node: any,
+     container: any,
+     anchor: any,
+     parentComponent: ComponentInternalInstance | null,
+   ): Node
+   hydrateSlot(vnode: VNode, node: any): Node
+   activate(
+     vnode: VNode,
+     container: any,
+     anchor: any,
+     parentComponent: ComponentInternalInstance,
+   ): void
+   deactivate(vnode: VNode, container: any): void
+   setTransitionHooks(
+     component: ComponentInternalInstance,
+     transition: TransitionHooks,
+   ): void
  
 -  vdomMount: (component: ConcreteComponent, props?: any, slots?: any) => any
 +  vdomMount: (
 +    component: ConcreteComponent,
 +    props?: any,
 +    slots?: any,
 +    scopeId?: string,
 +  ) => any
    vdomUnmount: UnmountComponentFn
    vdomSlot: (
      slots: any,
index 8e427d7cdabff6be255930f3870b2969e3c8a8de,b15fe1e6960bf0f9c294337a85d5f602604bd0eb..97ad17631135c945163f6c02afa109af2b1d5be1
@@@ -505,11 -516,7 +516,12 @@@ export { type VaporInteropInterface } f
  /**
   * @internal
   */
 -export { type RendererInternals, MoveType, invalidateMount } from './renderer'
 +export {
 +  type RendererInternals,
 +  MoveType,
 +  getInheritedScopeIds,
++  invalidateMount,
 +} from './renderer'
  /**
   * @internal
   */
Simple merge
index 9528e91de188a848db9de76aa246c4c721680444,1beeecf2d08a8359cbdeaee00fb4d3f26ec97f7c..e3b0c72a63d6e1e3b825dd97a9737e00c63c0af1
@@@ -550,972 -554,1349 +554,2316 @@@ describe('component: slots', () => 
        await nextTick()
        expect(host.innerHTML).toBe('<div><h1></h1><!--slot--></div>')
      })
+     test('render fallback when slot content is not valid', async () => {
+       const Child = {
+         setup() {
+           return createSlot('default', null, () =>
+             document.createTextNode('fallback'),
+           )
+         },
+       }
+       const { html } = define({
+         setup() {
+           return createComponent(Child, null, {
+             default: () => {
+               return template('<!--comment-->')()
+             },
+           })
+         },
+       }).render()
+       expect(html()).toBe('fallback<!--slot-->')
+     })
+     test('render fallback when v-if condition is false', async () => {
+       const Child = {
+         setup() {
+           return createSlot('default', null, () =>
+             document.createTextNode('fallback'),
+           )
+         },
+       }
+       const toggle = ref(false)
+       const { html } = define({
+         setup() {
+           return createComponent(Child, null, {
+             default: () => {
+               return createIf(
+                 () => toggle.value,
+                 () => {
+                   return document.createTextNode('content')
+                 },
+               )
+             },
+           })
+         },
+       }).render()
+       expect(html()).toBe('fallback<!--if--><!--slot-->')
+       toggle.value = true
+       await nextTick()
+       expect(html()).toBe('content<!--if--><!--slot-->')
+       toggle.value = false
+       await nextTick()
+       expect(html()).toBe('fallback<!--if--><!--slot-->')
+     })
+     test('render fallback with nested v-if', async () => {
+       const Child = {
+         setup() {
+           return createSlot('default', null, () =>
+             document.createTextNode('fallback'),
+           )
+         },
+       }
+       const outerShow = ref(false)
+       const innerShow = ref(false)
+       const { html } = define({
+         setup() {
+           return createComponent(Child, null, {
+             default: () => {
+               return createIf(
+                 () => outerShow.value,
+                 () => {
+                   return createIf(
+                     () => innerShow.value,
+                     () => {
+                       return document.createTextNode('content')
+                     },
+                   )
+                 },
+               )
+             },
+           })
+         },
+       }).render()
+       expect(html()).toBe('fallback<!--if--><!--slot-->')
+       outerShow.value = true
+       await nextTick()
+       expect(html()).toBe('fallback<!--if--><!--if--><!--slot-->')
+       innerShow.value = true
+       await nextTick()
+       expect(html()).toBe('content<!--if--><!--if--><!--slot-->')
+       innerShow.value = false
+       await nextTick()
+       expect(html()).toBe('fallback<!--if--><!--if--><!--slot-->')
+       outerShow.value = false
+       await nextTick()
+       expect(html()).toBe('fallback<!--if--><!--slot-->')
+       outerShow.value = true
+       await nextTick()
+       expect(html()).toBe('fallback<!--if--><!--if--><!--slot-->')
+       innerShow.value = true
+       await nextTick()
+       expect(html()).toBe('content<!--if--><!--if--><!--slot-->')
+     })
+     test('render fallback with v-for', async () => {
+       const Child = {
+         setup() {
+           return createSlot('default', null, () =>
+             document.createTextNode('fallback'),
+           )
+         },
+       }
+       const items = ref<number[]>([1])
+       const { html } = define({
+         setup() {
+           return createComponent(Child, null, {
+             default: () => {
+               const n2 = createFor(
+                 () => items.value,
+                 for_item0 => {
+                   const n4 = template('<span> </span>')() as any
+                   const x4 = child(n4) as any
+                   renderEffect(() =>
+                     setText(x4, toDisplayString(for_item0.value)),
+                   )
+                   return n4
+                 },
+               )
+               return n2
+             },
+           })
+         },
+       }).render()
+       expect(html()).toBe('<span>1</span><!--for--><!--slot-->')
+       items.value.pop()
+       await nextTick()
+       expect(html()).toBe('fallback<!--for--><!--slot-->')
+       items.value.pop()
+       await nextTick()
+       expect(html()).toBe('fallback<!--for--><!--slot-->')
+       items.value.push(2)
+       await nextTick()
+       expect(html()).toBe('<span>2</span><!--for--><!--slot-->')
+     })
+     test('render fallback with v-for (empty source)', async () => {
+       const Child = {
+         setup() {
+           return createSlot('default', null, () =>
+             document.createTextNode('fallback'),
+           )
+         },
+       }
+       const items = ref<number[]>([])
+       const { html } = define({
+         setup() {
+           return createComponent(Child, null, {
+             default: () => {
+               const n2 = createFor(
+                 () => items.value,
+                 for_item0 => {
+                   const n4 = template('<span> </span>')() as any
+                   const x4 = child(n4) as any
+                   renderEffect(() =>
+                     setText(x4, toDisplayString(for_item0.value)),
+                   )
+                   return n4
+                 },
+               )
+               return n2
+             },
+           })
+         },
+       }).render()
+       expect(html()).toBe('fallback<!--for--><!--slot-->')
+       items.value.push(1)
+       await nextTick()
+       expect(html()).toBe('<span>1</span><!--for--><!--slot-->')
+       items.value.pop()
+       await nextTick()
+       expect(html()).toBe('fallback<!--for--><!--slot-->')
+       items.value.pop()
+       await nextTick()
+       expect(html()).toBe('fallback<!--for--><!--slot-->')
+       items.value.push(2)
+       await nextTick()
+       expect(html()).toBe('<span>2</span><!--for--><!--slot-->')
+     })
+   })
+   describe('forwarded slot', () => {
+     test('should work', async () => {
+       const Child = defineVaporComponent({
+         setup() {
+           return createSlot('foo', null)
+         },
+       })
+       const Parent = defineVaporComponent({
+         setup() {
+           const createForwardedSlot = forwardedSlotCreator()
+           const n2 = createComponent(
+             Child,
+             null,
+             {
+               foo: () => {
+                 return createForwardedSlot('foo', null)
+               },
+             },
+             true,
+           )
+           return n2
+         },
+       })
+       const foo = ref('foo')
+       const { host } = define({
+         setup() {
+           const n2 = createComponent(
+             Parent,
+             null,
+             {
+               foo: () => {
+                 const n0 = template(' ')() as any
+                 renderEffect(() => setText(n0, foo.value))
+                 return n0
+               },
+             },
+             true,
+           )
+           return n2
+         },
+       }).render()
+       expect(host.innerHTML).toBe('foo<!--slot--><!--slot-->')
+       foo.value = 'bar'
+       await nextTick()
+       expect(host.innerHTML).toBe('bar<!--slot--><!--slot-->')
+     })
+     test('mixed with non-forwarded slot', async () => {
+       const Child = defineVaporComponent({
+         setup() {
+           return [createSlot('foo', null)]
+         },
+       })
+       const Parent = defineVaporComponent({
+         setup() {
+           const createForwardedSlot = forwardedSlotCreator()
+           const n2 = createComponent(Child, null, {
+             foo: () => {
+               const n0 = createForwardedSlot('foo', null)
+               return n0
+             },
+           })
+           const n3 = createSlot('default', null)
+           return [n2, n3]
+         },
+       })
+       const foo = ref('foo')
+       const { host } = define({
+         setup() {
+           const n2 = createComponent(
+             Parent,
+             null,
+             {
+               foo: () => {
+                 const n0 = template(' ')() as any
+                 renderEffect(() => setText(n0, foo.value))
+                 return n0
+               },
+               default: () => {
+                 const n3 = template(' ')() as any
+                 renderEffect(() => setText(n3, foo.value))
+                 return n3
+               },
+             },
+             true,
+           )
+           return n2
+         },
+       }).render()
+       expect(host.innerHTML).toBe('foo<!--slot--><!--slot-->foo<!--slot-->')
+       foo.value = 'bar'
+       await nextTick()
+       expect(host.innerHTML).toBe('bar<!--slot--><!--slot-->bar<!--slot-->')
+     })
+     test('forwarded slot with fallback', async () => {
+       const Child = defineVaporComponent({
+         setup() {
+           return createSlot('default', null, () => template('child fallback')())
+         },
+       })
+       const Parent = defineVaporComponent({
+         setup() {
+           const createForwardedSlot = forwardedSlotCreator()
+           const n2 = createComponent(Child, null, {
+             default: () => {
+               const n0 = createForwardedSlot('default', null, () => {
+                 return template('<!-- <div></div> -->')()
+               })
+               return n0
+             },
+           })
+           return n2
+         },
+       })
+       const { html } = define({
+         setup() {
+           return createComponent(Parent, null, {
+             default: () => template('<!-- <div>App</div> -->')(),
+           })
+         },
+       }).render()
+       expect(html()).toBe('child fallback<!--slot--><!--slot-->')
+     })
+     test('forwarded slot with fallback (v-if)', async () => {
+       const Child = defineVaporComponent({
+         setup() {
+           return createSlot('default', null, () => template('child fallback')())
+         },
+       })
+       const show = ref(false)
+       const Parent = defineVaporComponent({
+         setup() {
+           const createForwardedSlot = forwardedSlotCreator()
+           const n2 = createComponent(Child, null, {
+             default: () => {
+               const n0 = createForwardedSlot('default', null, () => {
+                 const n2 = createIf(
+                   () => show.value,
+                   () => {
+                     const n4 = template('<div>if content</div>')()
+                     return n4
+                   },
+                 )
+                 return n2
+               })
+               return n0
+             },
+           })
+           return n2
+         },
+       })
+       const { html } = define({
+         setup() {
+           return createComponent(Parent, null, {
+             default: () => template('<!-- <div>App</div> -->')(),
+           })
+         },
+       }).render()
+       expect(html()).toBe('child fallback<!--if--><!--slot--><!--slot-->')
+       show.value = true
+       await nextTick()
+       expect(html()).toBe(
+         '<div>if content</div><!--if--><!--slot--><!--slot-->',
+       )
+     })
+     test('forwarded slot with fallback (v-for)', async () => {
+       const Child = defineVaporComponent({
+         setup() {
+           return createSlot('default', null, () => template('child fallback')())
+         },
+       })
+       const items = ref<number[]>([])
+       const Parent = defineVaporComponent({
+         setup() {
+           const createForwardedSlot = forwardedSlotCreator()
+           const n2 = createComponent(Child, null, {
+             default: () => {
+               const n0 = createForwardedSlot('default', null, () => {
+                 const n2 = createFor(
+                   () => items.value,
+                   for_item0 => {
+                     const n4 = template('<span> </span>')() as any
+                     const x4 = child(n4) as any
+                     renderEffect(() =>
+                       setText(x4, toDisplayString(for_item0.value)),
+                     )
+                     return n4
+                   },
+                 )
+                 return n2
+               })
+               return n0
+             },
+           })
+           return n2
+         },
+       })
+       const { html } = define({
+         setup() {
+           return createComponent(Parent, null, {
+             default: () => template('<!-- <div>App</div> -->')(),
+           })
+         },
+       }).render()
+       expect(html()).toBe('child fallback<!--for--><!--slot--><!--slot-->')
+       items.value.push(1)
+       await nextTick()
+       expect(html()).toBe('<span>1</span><!--for--><!--slot--><!--slot-->')
+       items.value.pop()
+       await nextTick()
+       expect(html()).toBe('child fallback<!--for--><!--slot--><!--slot-->')
+     })
+     describe('vdom interop', () => {
+       const createVaporSlot = (fallbackText = 'fallback') => {
+         return defineVaporComponent({
+           setup() {
+             const n0 = createSlot('foo', null, () => {
+               const n2 = template(`<div>${fallbackText}</div>`)()
+               return n2
+             })
+             return n0
+           },
+         })
+       }
+       const createVdomSlot = (fallbackText = 'fallback') => {
+         return {
+           render(this: any) {
+             return renderSlot(this.$slots, 'foo', {}, () => [
+               h('div', fallbackText),
+             ])
+           },
+         }
+       }
+       const createVaporForwardedSlot = (
+         targetComponent: any,
+         fallbackText?: string,
+       ) => {
+         return defineVaporComponent({
+           setup() {
+             const createForwardedSlot = forwardedSlotCreator()
+             const n2 = createComponent(
+               targetComponent,
+               null,
+               {
+                 foo: () => {
+                   return fallbackText
+                     ? createForwardedSlot('foo', null, () => {
+                         const n2 = template(`<div>${fallbackText}</div>`)()
+                         return n2
+                       })
+                     : createForwardedSlot('foo', null)
+                 },
+               },
+               true,
+             )
+             return n2
+           },
+         })
+       }
+       const createVdomForwardedSlot = (
+         targetComponent: any,
+         fallbackText?: string,
+       ) => {
+         return {
+           render(this: any) {
+             return h(targetComponent, null, {
+               foo: () => [
+                 fallbackText
+                   ? renderSlot(this.$slots, 'foo', {}, () => [
+                       h('div', fallbackText),
+                     ])
+                   : renderSlot(this.$slots, 'foo'),
+               ],
+               _: 3 /* FORWARDED */,
+             })
+           },
+         }
+       }
+       const createMultipleVaporForwardedSlots = (
+         targetComponent: any,
+         count: number,
+       ) => {
+         let current = targetComponent
+         for (let i = 0; i < count; i++) {
+           current = createVaporForwardedSlot(current)
+         }
+         return current
+       }
+       const createMultipleVdomForwardedSlots = (
+         targetComponent: any,
+         count: number,
+       ) => {
+         let current = targetComponent
+         for (let i = 0; i < count; i++) {
+           current = createVdomForwardedSlot(current)
+         }
+         return current
+       }
+       const createTestApp = (
+         rootComponent: any,
+         foo: Ref<string>,
+         show: Ref<Boolean>,
+       ) => {
+         return {
+           setup() {
+             return () =>
+               h(
+                 rootComponent,
+                 null,
+                 createSlots({ _: 2 /* DYNAMIC */ } as any, [
+                   show.value
+                     ? {
+                         name: 'foo',
+                         fn: () => [h('span', foo.value)],
+                         key: '0',
+                       }
+                     : undefined,
+                 ]),
+               )
+           },
+         }
+       }
+       const createEmptyTestApp = (rootComponent: any) => {
+         return {
+           setup() {
+             return () => h(rootComponent)
+           },
+         }
+       }
+       test('vdom slot > vapor forwarded slot > vapor slot', async () => {
+         const foo = ref('foo')
+         const show = ref(true)
+         const VaporSlot = createVaporSlot()
+         const VaporForwardedSlot = createVaporForwardedSlot(VaporSlot)
+         const App = createTestApp(VaporForwardedSlot, foo, show)
+         const root = document.createElement('div')
+         createApp(App).use(vaporInteropPlugin).mount(root)
+         expect(root.innerHTML).toBe('<span>foo</span><!--slot-->')
+         foo.value = 'bar'
+         await nextTick()
+         expect(root.innerHTML).toBe('<span>bar</span><!--slot-->')
+         show.value = false
+         await nextTick()
+         expect(root.innerHTML).toBe('<div>fallback</div><!--slot-->')
+       })
+       test('vdom slot > vapor forwarded slot(with fallback) > vapor slot', async () => {
+         const foo = ref('foo')
+         const show = ref(true)
+         const VaporSlot = createVaporSlot()
+         const VaporForwardedSlotWithFallback = createVaporForwardedSlot(
+           VaporSlot,
+           'forwarded fallback',
+         )
+         const App = createTestApp(VaporForwardedSlotWithFallback, foo, show)
+         const root = document.createElement('div')
+         createApp(App).use(vaporInteropPlugin).mount(root)
+         expect(root.innerHTML).toBe('<span>foo</span><!--slot-->')
+         foo.value = 'bar'
+         await nextTick()
+         expect(root.innerHTML).toBe('<span>bar</span><!--slot-->')
+         show.value = false
+         await nextTick()
+         expect(root.innerHTML).toBe('<div>forwarded fallback</div><!--slot-->')
+       })
+       test('vdom slot > vapor forwarded slot > vdom slot', async () => {
+         const foo = ref('foo')
+         const show = ref(true)
+         const VdomSlot = createVdomSlot()
+         const VaporForwardedSlot = createVaporForwardedSlot(VdomSlot)
+         const App = createTestApp(VaporForwardedSlot, foo, show)
+         const root = document.createElement('div')
+         createApp(App).use(vaporInteropPlugin).mount(root)
+         expect(root.innerHTML).toBe('<span>foo</span>')
+         foo.value = 'bar'
+         await nextTick()
+         expect(root.innerHTML).toBe('<span>bar</span>')
+         show.value = false
+         await nextTick()
+         expect(root.innerHTML).toBe('<div>fallback</div>')
+         show.value = true
+         await nextTick()
+         expect(root.innerHTML).toBe('<span>bar</span>')
+       })
+       test('vdom slot > vapor forwarded slot(with fallback) > vdom slot', async () => {
+         const foo = ref('foo')
+         const show = ref(true)
+         const VdomSlot = createVdomSlot()
+         const VaporForwardedSlotWithFallback = createVaporForwardedSlot(
+           VdomSlot,
+           'forwarded fallback',
+         )
+         const App = createTestApp(VaporForwardedSlotWithFallback, foo, show)
+         const root = document.createElement('div')
+         createApp(App).use(vaporInteropPlugin).mount(root)
+         expect(root.innerHTML).toBe('<span>foo</span>')
+         foo.value = 'bar'
+         await nextTick()
+         expect(root.innerHTML).toBe('<span>bar</span>')
+         show.value = false
+         await nextTick()
+         expect(root.innerHTML).toBe('<div>forwarded fallback</div>')
+       })
+       test('vdom slot > vapor forwarded slot > vdom forwarded slot > vapor slot', async () => {
+         const foo = ref('foo')
+         const show = ref(true)
+         const VaporSlot = createVaporSlot()
+         const VdomForwardedSlot = createVdomForwardedSlot(VaporSlot)
+         const VaporForwardedSlot = createVaporForwardedSlot(VdomForwardedSlot)
+         const App = createTestApp(VaporForwardedSlot, foo, show)
+         const root = document.createElement('div')
+         createApp(App).use(vaporInteropPlugin).mount(root)
+         expect(root.innerHTML).toBe('<span>foo</span>')
+         foo.value = 'bar'
+         await nextTick()
+         expect(root.innerHTML).toBe('<span>bar</span>')
+         show.value = false
+         await nextTick()
+         expect(root.innerHTML).toBe('<div>fallback</div>')
+         show.value = true
+         await nextTick()
+         expect(root.innerHTML).toBe('<span>bar</span>')
+       })
+       test('vdom slot > vapor forwarded slot(with fallback) > vdom forwarded slot > vapor slot', async () => {
+         const foo = ref('foo')
+         const show = ref(true)
+         const VaporSlot = createVaporSlot()
+         const VdomForwardedSlot = createVdomForwardedSlot(VaporSlot)
+         const VaporForwardedSlotWithFallback = createVaporForwardedSlot(
+           VdomForwardedSlot,
+           'forwarded fallback',
+         )
+         const App = createTestApp(VaporForwardedSlotWithFallback, foo, show)
+         const root = document.createElement('div')
+         createApp(App).use(vaporInteropPlugin).mount(root)
+         expect(root.innerHTML).toBe('<span>foo</span>')
+         foo.value = 'bar'
+         await nextTick()
+         expect(root.innerHTML).toBe('<span>bar</span>')
+         show.value = false
+         await nextTick()
+         expect(root.innerHTML).toBe('<div>forwarded fallback</div>')
+         show.value = true
+         await nextTick()
+         expect(root.innerHTML).toBe('<span>bar</span>')
+       })
+       test('vdom slot > vapor forwarded slot > vdom forwarded slot(with fallback) > vapor slot', async () => {
+         const foo = ref('foo')
+         const show = ref(true)
+         const VaporSlot = createVaporSlot()
+         const VdomForwardedSlotWithFallback = createVdomForwardedSlot(
+           VaporSlot,
+           'vdom fallback',
+         )
+         const VaporForwardedSlot = createVaporForwardedSlot(
+           VdomForwardedSlotWithFallback,
+         )
+         const App = createTestApp(VaporForwardedSlot, foo, show)
+         const root = document.createElement('div')
+         createApp(App).use(vaporInteropPlugin).mount(root)
+         expect(root.innerHTML).toBe('<span>foo</span>')
+         foo.value = 'bar'
+         await nextTick()
+         expect(root.innerHTML).toBe('<span>bar</span>')
+         show.value = false
+         await nextTick()
+         expect(root.innerHTML).toBe('<div>vdom fallback</div>')
+         show.value = true
+         await nextTick()
+         expect(root.innerHTML).toBe('<span>bar</span>')
+       })
+       test('vdom slot(empty) > vapor forwarded slot > vdom forwarded slot(with fallback) > vapor slot', async () => {
+         const VaporSlot = createVaporSlot()
+         const VdomForwardedSlotWithFallback = createVdomForwardedSlot(
+           VaporSlot,
+           'vdom fallback',
+         )
+         const VaporForwardedSlot = createVaporForwardedSlot(
+           VdomForwardedSlotWithFallback,
+         )
+         const App = createEmptyTestApp(VaporForwardedSlot)
+         const root = document.createElement('div')
+         createApp(App).use(vaporInteropPlugin).mount(root)
+         expect(root.innerHTML).toBe('<div>vdom fallback</div>')
+       })
+       test('vdom slot > vapor forwarded slot > vdom forwarded slot > vdom slot', async () => {
+         const foo = ref('foo')
+         const show = ref(true)
+         const VdomSlot = createVdomSlot()
+         const VdomForwardedSlot = createVdomForwardedSlot(VdomSlot)
+         const VaporForwardedSlot = createVaporForwardedSlot(VdomForwardedSlot)
+         const App = createTestApp(VaporForwardedSlot, foo, show)
+         const root = document.createElement('div')
+         createApp(App).use(vaporInteropPlugin).mount(root)
+         expect(root.innerHTML).toBe('<span>foo</span>')
+         foo.value = 'bar'
+         await nextTick()
+         expect(root.innerHTML).toBe('<span>bar</span>')
+         show.value = false
+         await nextTick()
+         expect(root.innerHTML).toBe('<div>fallback</div>')
+         show.value = true
+         await nextTick()
+         expect(root.innerHTML).toBe('<span>bar</span>')
+       })
+       test('vdom slot > vapor forwarded slot(with fallback) > vdom forwarded slot > vdom slot', async () => {
+         const foo = ref('foo')
+         const show = ref(true)
+         const VdomSlot = createVdomSlot()
+         const VdomForwardedSlot = createVdomForwardedSlot(VdomSlot)
+         const VaporForwardedSlotWithFallback = createVaporForwardedSlot(
+           VdomForwardedSlot,
+           'vapor fallback',
+         )
+         const App = createTestApp(VaporForwardedSlotWithFallback, foo, show)
+         const root = document.createElement('div')
+         createApp(App).use(vaporInteropPlugin).mount(root)
+         expect(root.innerHTML).toBe('<span>foo</span>')
+         foo.value = 'bar'
+         await nextTick()
+         expect(root.innerHTML).toBe('<span>bar</span>')
+         show.value = false
+         await nextTick()
+         expect(root.innerHTML).toBe('<div>vapor fallback</div>')
+         show.value = true
+         await nextTick()
+         expect(root.innerHTML).toBe('<span>bar</span>')
+       })
+       test('vdom slot > vapor forwarded slot > vdom forwarded slot(with fallback) > vdom slot', async () => {
+         const foo = ref('foo')
+         const show = ref(true)
+         const VdomSlot = createVdomSlot()
+         const VdomForwardedSlotWithFallback = createVdomForwardedSlot(
+           VdomSlot,
+           'vdom fallback',
+         )
+         const VaporForwardedSlot = createVaporForwardedSlot(
+           VdomForwardedSlotWithFallback,
+         )
+         const App = createTestApp(VaporForwardedSlot, foo, show)
+         const root = document.createElement('div')
+         createApp(App).use(vaporInteropPlugin).mount(root)
+         expect(root.innerHTML).toBe('<span>foo</span>')
+         foo.value = 'bar'
+         await nextTick()
+         expect(root.innerHTML).toBe('<span>bar</span>')
+         show.value = false
+         await nextTick()
+         expect(root.innerHTML).toBe('<div>vdom fallback</div>')
+         show.value = true
+         await nextTick()
+         expect(root.innerHTML).toBe('<span>bar</span>')
+       })
+       test('vdom slot > vapor forwarded slot (multiple) > vdom forwarded slot > vdom slot', async () => {
+         const foo = ref('foo')
+         const show = ref(true)
+         const VdomSlot = createVdomSlot()
+         const VdomForwardedSlot = createVdomForwardedSlot(VdomSlot)
+         const VaporForwardedSlot = createMultipleVaporForwardedSlots(
+           VdomForwardedSlot,
+           3,
+         )
+         const App = createTestApp(VaporForwardedSlot, foo, show)
+         const root = document.createElement('div')
+         createApp(App).use(vaporInteropPlugin).mount(root)
+         expect(root.innerHTML).toBe('<span>foo</span><!--slot--><!--slot-->')
+         foo.value = 'bar'
+         await nextTick()
+         expect(root.innerHTML).toBe('<span>bar</span><!--slot--><!--slot-->')
+         show.value = false
+         await nextTick()
+         expect(root.innerHTML).toBe('<div>fallback</div><!--slot--><!--slot-->')
+         show.value = true
+         await nextTick()
+         expect(root.innerHTML).toBe('<span>bar</span><!--slot--><!--slot-->')
+       })
+       test('vdom slot > vapor forwarded slot (multiple) > vdom forwarded slot(with fallback) > vdom slot', async () => {
+         const foo = ref('foo')
+         const show = ref(true)
+         const VdomSlot = createVdomSlot()
+         const VdomForwardedSlotWithFallback = createVdomForwardedSlot(
+           VdomSlot,
+           'vdom fallback',
+         )
+         const VaporForwardedSlot = createMultipleVaporForwardedSlots(
+           VdomForwardedSlotWithFallback,
+           3,
+         )
+         const App = createTestApp(VaporForwardedSlot, foo, show)
+         const root = document.createElement('div')
+         createApp(App).use(vaporInteropPlugin).mount(root)
+         expect(root.innerHTML).toBe('<span>foo</span><!--slot--><!--slot-->')
+         foo.value = 'bar'
+         await nextTick()
+         expect(root.innerHTML).toBe('<span>bar</span><!--slot--><!--slot-->')
+         show.value = false
+         await nextTick()
+         expect(root.innerHTML).toBe(
+           '<div>vdom fallback</div><!--slot--><!--slot-->',
+         )
+         show.value = true
+         await nextTick()
+         expect(root.innerHTML).toBe('<span>bar</span><!--slot--><!--slot-->')
+       })
+       test('vdom slot > vdom forwarded slot > vapor slot', async () => {
+         const foo = ref('foo')
+         const show = ref(true)
+         const VaporSlot = createVaporSlot()
+         const VdomForwardedSlot = createVdomForwardedSlot(VaporSlot)
+         const App = createTestApp(VdomForwardedSlot, foo, show)
+         const root = document.createElement('div')
+         createApp(App).use(vaporInteropPlugin).mount(root)
+         expect(root.innerHTML).toBe('<span>foo</span>')
+         foo.value = 'bar'
+         await nextTick()
+         expect(root.innerHTML).toBe('<span>bar</span>')
+         show.value = false
+         await nextTick()
+         expect(root.innerHTML).toBe('<div>fallback</div>')
+         show.value = true
+         await nextTick()
+         expect(root.innerHTML).toBe('<span>bar</span>')
+       })
+       test('vdom slot > vdom forwarded slot > vapor forwarded slot > vapor slot', async () => {
+         const foo = ref('foo')
+         const show = ref(true)
+         const VaporSlot = createVaporSlot()
+         const VaporForwardedSlot = createVaporForwardedSlot(VaporSlot)
+         const VdomForwardedSlot = createVdomForwardedSlot(VaporForwardedSlot)
+         const App = createTestApp(VdomForwardedSlot, foo, show)
+         const root = document.createElement('div')
+         createApp(App).use(vaporInteropPlugin).mount(root)
+         expect(root.innerHTML).toBe('<span>foo</span><!--slot-->')
+         foo.value = 'bar'
+         await nextTick()
+         expect(root.innerHTML).toBe('<span>bar</span><!--slot-->')
+         show.value = false
+         await nextTick()
+         expect(root.innerHTML).toBe('<div>fallback</div><!--slot-->')
+         show.value = true
+         await nextTick()
+         expect(root.innerHTML).toBe('<span>bar</span><!--slot-->')
+       })
+       test('vdom slot > vdom forwarded slot (multiple) > vapor forwarded slot > vdom slot', async () => {
+         const foo = ref('foo')
+         const show = ref(true)
+         const VaporSlot = createVaporSlot()
+         const VaporForwardedSlot = createVaporForwardedSlot(VaporSlot)
+         const VdomForwardedSlot = createMultipleVdomForwardedSlots(
+           VaporForwardedSlot,
+           3,
+         )
+         const App = createTestApp(VdomForwardedSlot, foo, show)
+         const root = document.createElement('div')
+         createApp(App).use(vaporInteropPlugin).mount(root)
+         expect(root.innerHTML).toBe('<span>foo</span><!--slot-->')
+         foo.value = 'bar'
+         await nextTick()
+         expect(root.innerHTML).toBe('<span>bar</span><!--slot-->')
+         show.value = false
+         await nextTick()
+         expect(root.innerHTML).toBe('<div>fallback</div><!--slot-->')
+         show.value = true
+         await nextTick()
+         expect(root.innerHTML).toBe('<span>bar</span><!--slot-->')
+       })
+       test('vdom slot > vdom forwarded slot (multiple) > vapor forwarded slot(with fallback) > vdom slot', async () => {
+         const foo = ref('foo')
+         const show = ref(true)
+         const VaporSlot = createVaporSlot()
+         const VaporForwardedSlot = createVaporForwardedSlot(
+           VaporSlot,
+           'vapor fallback',
+         )
+         const VdomForwardedSlot = createMultipleVdomForwardedSlots(
+           VaporForwardedSlot,
+           3,
+         )
+         const App = createTestApp(VdomForwardedSlot, foo, show)
+         const root = document.createElement('div')
+         createApp(App).use(vaporInteropPlugin).mount(root)
+         expect(root.innerHTML).toBe('<span>foo</span><!--slot-->')
+         foo.value = 'bar'
+         await nextTick()
+         expect(root.innerHTML).toBe('<span>bar</span><!--slot-->')
+         show.value = false
+         await nextTick()
+         expect(root.innerHTML).toBe('<div>vapor fallback</div><!--slot-->')
+         show.value = true
+         await nextTick()
+         expect(root.innerHTML).toBe('<span>bar</span><!--slot-->')
+       })
+       test('vdom slot > vapor forwarded slot > vapor forwarded slot > vdom slot', async () => {
+         const foo = ref('foo')
+         const show = ref(true)
+         const VdomSlot = createVdomSlot()
+         const VaporForwardedSlot1 = createMultipleVaporForwardedSlots(
+           VdomSlot,
+           2,
+         )
+         const App = createTestApp(VaporForwardedSlot1, foo, show)
+         const root = document.createElement('div')
+         createApp(App).use(vaporInteropPlugin).mount(root)
+         expect(root.innerHTML).toBe('<span>foo</span><!--slot-->')
+         foo.value = 'bar'
+         await nextTick()
+         expect(root.innerHTML).toBe('<span>bar</span><!--slot-->')
+         show.value = false
+         await nextTick()
+         expect(root.innerHTML).toBe('<div>fallback</div><!--slot-->')
+         show.value = true
+         await nextTick()
+         expect(root.innerHTML).toBe('<span>bar</span><!--slot-->')
+       })
+       test('vdom slot > vapor forwarded slot(with fallback) > vapor forwarded slot > vdom slot', async () => {
+         const foo = ref('foo')
+         const show = ref(true)
+         const VdomSlot = createVdomSlot()
+         const VaporForwardedSlot2 = createVaporForwardedSlot(VdomSlot)
+         const VaporForwardedSlot1WithFallback = createVaporForwardedSlot(
+           VaporForwardedSlot2,
+           'vapor1 fallback',
+         )
+         const App = createTestApp(VaporForwardedSlot1WithFallback, foo, show)
+         const root = document.createElement('div')
+         createApp(App).use(vaporInteropPlugin).mount(root)
+         expect(root.innerHTML).toBe('<span>foo</span><!--slot-->')
+         foo.value = 'bar'
+         await nextTick()
+         expect(root.innerHTML).toBe('<span>bar</span><!--slot-->')
+         show.value = false
+         await nextTick()
+         expect(root.innerHTML).toBe('<div>vapor1 fallback</div><!--slot-->')
+         show.value = true
+         await nextTick()
+         expect(root.innerHTML).toBe('<span>bar</span><!--slot-->')
+       })
+       test('vdom slot > vapor forwarded slot > vapor forwarded slot(with fallback) > vdom slot', async () => {
+         const foo = ref('foo')
+         const show = ref(true)
+         const VdomSlot = createVdomSlot()
+         const VaporForwardedSlot2WithFallback = createVaporForwardedSlot(
+           VdomSlot,
+           'vapor2 fallback',
+         )
+         const VaporForwardedSlot1 = createVaporForwardedSlot(
+           VaporForwardedSlot2WithFallback,
+         )
+         const App = createTestApp(VaporForwardedSlot1, foo, show)
+         const root = document.createElement('div')
+         createApp(App).use(vaporInteropPlugin).mount(root)
+         expect(root.innerHTML).toBe('<span>foo</span><!--slot-->')
+         foo.value = 'bar'
+         await nextTick()
+         expect(root.innerHTML).toBe('<span>bar</span><!--slot-->')
+         show.value = false
+         await nextTick()
+         expect(root.innerHTML).toBe('<div>vapor2 fallback</div><!--slot-->')
+         show.value = true
+         await nextTick()
+         expect(root.innerHTML).toBe('<span>bar</span><!--slot-->')
+       })
+       test('vdom slot > vapor forwarded slot > vapor forwarded slot > vapor slot', async () => {
+         const foo = ref('foo')
+         const show = ref(true)
+         const VaporSlot = createVaporSlot()
+         const VaporForwardedSlot2 = createVaporForwardedSlot(VaporSlot)
+         const VaporForwardedSlot1 =
+           createVaporForwardedSlot(VaporForwardedSlot2)
+         const App = createTestApp(VaporForwardedSlot1, foo, show)
+         const root = document.createElement('div')
+         createApp(App).use(vaporInteropPlugin).mount(root)
+         expect(root.innerHTML).toBe('<span>foo</span><!--slot--><!--slot-->')
+         foo.value = 'bar'
+         await nextTick()
+         expect(root.innerHTML).toBe('<span>bar</span><!--slot--><!--slot-->')
+         show.value = false
+         await nextTick()
+         expect(root.innerHTML).toBe('<div>fallback</div><!--slot--><!--slot-->')
+         show.value = true
+         await nextTick()
+         expect(root.innerHTML).toBe('<span>bar</span><!--slot--><!--slot-->')
+       })
+       test('vdom slot > vapor forwarded slot(with fallback) > vapor forwarded slot(with fallback) > vdom slot', async () => {
+         const foo = ref('foo')
+         const show = ref(true)
+         const VdomSlot = createVdomSlot()
+         const VaporForwardedSlot2WithFallback = createVaporForwardedSlot(
+           VdomSlot,
+           'vapor2 fallback',
+         )
+         const VaporForwardedSlot1WithFallback = createVaporForwardedSlot(
+           VaporForwardedSlot2WithFallback,
+           'vapor1 fallback',
+         )
+         const App = createTestApp(VaporForwardedSlot1WithFallback, foo, show)
+         const root = document.createElement('div')
+         createApp(App).use(vaporInteropPlugin).mount(root)
+         expect(root.innerHTML).toBe('<span>foo</span><!--slot-->')
+         foo.value = 'bar'
+         await nextTick()
+         expect(root.innerHTML).toBe('<span>bar</span><!--slot-->')
+         show.value = false
+         await nextTick()
+         expect(root.innerHTML).toBe('<div>vapor1 fallback</div><!--slot-->')
+         show.value = true
+         await nextTick()
+         expect(root.innerHTML).toBe('<span>bar</span><!--slot-->')
+       })
+       test('vdom slot > vapor forwarded slot(with fallback) > vapor forwarded slot(with fallback) > vapor slot', async () => {
+         const foo = ref('foo')
+         const show = ref(true)
+         const VaporSlot = createVaporSlot()
+         const VaporForwardedSlot2WithFallback = createVaporForwardedSlot(
+           VaporSlot,
+           'vapor2 fallback',
+         )
+         const VaporForwardedSlot1WithFallback = createVaporForwardedSlot(
+           VaporForwardedSlot2WithFallback,
+           'vapor1 fallback',
+         )
+         const App = createTestApp(VaporForwardedSlot1WithFallback, foo, show)
+         const root = document.createElement('div')
+         createApp(App).use(vaporInteropPlugin).mount(root)
+         expect(root.innerHTML).toBe('<span>foo</span><!--slot--><!--slot-->')
+         foo.value = 'bar'
+         await nextTick()
+         expect(root.innerHTML).toBe('<span>bar</span><!--slot--><!--slot-->')
+         show.value = false
+         await nextTick()
+         expect(root.innerHTML).toBe(
+           '<div>vapor1 fallback</div><!--slot--><!--slot-->',
+         )
+         show.value = true
+         await nextTick()
+         expect(root.innerHTML).toBe('<span>bar</span><!--slot--><!--slot-->')
+       })
+       test('vdom slot > vdom forwarded slot(with fallback) > vdom forwarded slot(with fallback) > vapor slot', async () => {
+         const foo = ref('foo')
+         const show = ref(true)
+         const VaporSlot = createVaporSlot()
+         const VdomForwardedSlot2WithFallback = createVdomForwardedSlot(
+           VaporSlot,
+           'vdom2 fallback',
+         )
+         const VdomForwardedSlot1WithFallback = createVdomForwardedSlot(
+           VdomForwardedSlot2WithFallback,
+           'vdom1 fallback',
+         )
+         const App = createTestApp(VdomForwardedSlot1WithFallback, foo, show)
+         const root = document.createElement('div')
+         createApp(App).use(vaporInteropPlugin).mount(root)
+         expect(root.innerHTML).toBe('<span>foo</span>')
+         foo.value = 'bar'
+         await nextTick()
+         expect(root.innerHTML).toBe('<span>bar</span>')
+         show.value = false
+         await nextTick()
+         expect(root.innerHTML).toBe('<div>vdom1 fallback</div>')
+         show.value = true
+         await nextTick()
+         expect(root.innerHTML).toBe('<span>bar</span>')
+       })
+       test('vdom slot > vdom forwarded slot(with fallback) > vdom forwarded slot(with fallback) > vdom slot', async () => {
+         const foo = ref('foo')
+         const show = ref(true)
+         const VdomSlot = createVdomSlot()
+         const VdomForwardedSlot2WithFallback = createVdomForwardedSlot(
+           VdomSlot,
+           'vdom2 fallback',
+         )
+         const VdomForwardedSlot1WithFallback = createVdomForwardedSlot(
+           VdomForwardedSlot2WithFallback,
+           'vdom1 fallback',
+         )
+         const App = createTestApp(VdomForwardedSlot1WithFallback, foo, show)
+         const root = document.createElement('div')
+         createApp(App).use(vaporInteropPlugin).mount(root)
+         expect(root.innerHTML).toBe('<span>foo</span>')
+         foo.value = 'bar'
+         await nextTick()
+         expect(root.innerHTML).toBe('<span>bar</span>')
+         show.value = false
+         await nextTick()
+         expect(root.innerHTML).toBe('<div>vdom1 fallback</div>')
+         show.value = true
+         await nextTick()
+         expect(root.innerHTML).toBe('<span>bar</span>')
+       })
+       test('vdom slot > vdom forwarded slot(with fallback) > vdom forwarded slot(with fallback) (multiple) > vapor slot', async () => {
+         const foo = ref('foo')
+         const show = ref(true)
+         const VaporSlot = createVaporSlot()
+         const VdomForwardedSlot3WithFallback = createVdomForwardedSlot(
+           VaporSlot,
+           'vdom3 fallback',
+         )
+         const VdomForwardedSlot2WithFallback = createVdomForwardedSlot(
+           VdomForwardedSlot3WithFallback,
+           'vdom2 fallback',
+         )
+         const VdomForwardedSlot1WithFallback = createVdomForwardedSlot(
+           VdomForwardedSlot2WithFallback,
+           'vdom1 fallback',
+         )
+         const App = createTestApp(VdomForwardedSlot1WithFallback, foo, show)
+         const root = document.createElement('div')
+         createApp(App).use(vaporInteropPlugin).mount(root)
+         expect(root.innerHTML).toBe('<span>foo</span>')
+         foo.value = 'bar'
+         await nextTick()
+         expect(root.innerHTML).toBe('<span>bar</span>')
+         show.value = false
+         await nextTick()
+         expect(root.innerHTML).toBe('<div>vdom1 fallback</div>')
+         show.value = true
+         await nextTick()
+         expect(root.innerHTML).toBe('<span>bar</span>')
+       })
+     })
+     test('consecutive slots with insertion state', async () => {
+       const { component: Child } = define({
+         setup() {
+           const n2 = template('<div><div>baz</div></div>', true)() as any
+           setInsertionState(n2, 0)
+           createSlot('default', null)
+           setInsertionState(n2, 0)
+           createSlot('foo', null)
+           return n2
+         },
+       })
+       const { html } = define({
+         setup() {
+           return createComponent(Child, null, {
+             default: () => template('default')(),
+             foo: () => template('foo')(),
+           })
+         },
+       }).render()
+       expect(html()).toBe(
+         `<div>` +
+           `default<!--slot-->` +
+           `foo<!--slot-->` +
+           `<div>baz</div>` +
+           `</div>`,
+       )
+     })
    })
 +
 +  describe('forwarded slot', () => {
 +    test('should work', async () => {
 +      const Child = defineVaporComponent({
 +        setup() {
 +          return createSlot('foo', null)
 +        },
 +      })
 +      const Parent = defineVaporComponent({
 +        setup() {
 +          const createForwardedSlot = forwardedSlotCreator()
 +          const n2 = createComponent(
 +            Child,
 +            null,
 +            {
 +              foo: () => {
 +                return createForwardedSlot('foo', null)
 +              },
 +            },
 +            true,
 +          )
 +          return n2
 +        },
 +      })
 +
 +      const foo = ref('foo')
 +      const { host } = define({
 +        setup() {
 +          const n2 = createComponent(
 +            Parent,
 +            null,
 +            {
 +              foo: () => {
 +                const n0 = template(' ')() as any
 +                renderEffect(() => setText(n0, foo.value))
 +                return n0
 +              },
 +            },
 +            true,
 +          )
 +          return n2
 +        },
 +      }).render()
 +
 +      expect(host.innerHTML).toBe('foo<!--slot--><!--slot-->')
 +
 +      foo.value = 'bar'
 +      await nextTick()
 +      expect(host.innerHTML).toBe('bar<!--slot--><!--slot-->')
 +    })
 +
 +    test('mixed with non-forwarded slot', async () => {
 +      const Child = defineVaporComponent({
 +        setup() {
 +          return [createSlot('foo', null)]
 +        },
 +      })
 +      const Parent = defineVaporComponent({
 +        setup() {
 +          const createForwardedSlot = forwardedSlotCreator()
 +          const n2 = createComponent(Child, null, {
 +            foo: () => {
 +              const n0 = createForwardedSlot('foo', null)
 +              return n0
 +            },
 +          })
 +          const n3 = createSlot('default', null)
 +          return [n2, n3]
 +        },
 +      })
 +
 +      const foo = ref('foo')
 +      const { host } = define({
 +        setup() {
 +          const n2 = createComponent(
 +            Parent,
 +            null,
 +            {
 +              foo: () => {
 +                const n0 = template(' ')() as any
 +                renderEffect(() => setText(n0, foo.value))
 +                return n0
 +              },
 +              default: () => {
 +                const n3 = template(' ')() as any
 +                renderEffect(() => setText(n3, foo.value))
 +                return n3
 +              },
 +            },
 +            true,
 +          )
 +          return n2
 +        },
 +      }).render()
 +
 +      expect(host.innerHTML).toBe('foo<!--slot--><!--slot-->foo<!--slot-->')
 +
 +      foo.value = 'bar'
 +      await nextTick()
 +      expect(host.innerHTML).toBe('bar<!--slot--><!--slot-->bar<!--slot-->')
 +    })
 +
 +    describe('vdom interop', () => {
 +      const createVaporSlot = (fallbackText = 'fallback') => {
 +        return defineVaporComponent({
 +          setup() {
 +            const n0 = createSlot('foo', null, () => {
 +              const n2 = template(`<div>${fallbackText}</div>`)()
 +              return n2
 +            })
 +            return n0
 +          },
 +        })
 +      }
 +
 +      const createVdomSlot = (fallbackText = 'fallback') => {
 +        return {
 +          render(this: any) {
 +            return renderSlot(this.$slots, 'foo', {}, () => [
 +              h('div', fallbackText),
 +            ])
 +          },
 +        }
 +      }
 +
 +      const createVaporForwardedSlot = (
 +        targetComponent: any,
 +        fallbackText?: string,
 +      ) => {
 +        return defineVaporComponent({
 +          setup() {
 +            const createForwardedSlot = forwardedSlotCreator()
 +            const n2 = createComponent(
 +              targetComponent,
 +              null,
 +              {
 +                foo: () => {
 +                  return fallbackText
 +                    ? createForwardedSlot('foo', null, () => {
 +                        const n2 = template(`<div>${fallbackText}</div>`)()
 +                        return n2
 +                      })
 +                    : createForwardedSlot('foo', null)
 +                },
 +              },
 +              true,
 +            )
 +            return n2
 +          },
 +        })
 +      }
 +
 +      const createVdomForwardedSlot = (
 +        targetComponent: any,
 +        fallbackText?: string,
 +      ) => {
 +        return {
 +          render(this: any) {
 +            return h(targetComponent, null, {
 +              foo: () => [
 +                fallbackText
 +                  ? renderSlot(this.$slots, 'foo', {}, () => [
 +                      h('div', fallbackText),
 +                    ])
 +                  : renderSlot(this.$slots, 'foo'),
 +              ],
 +              _: 3 /* FORWARDED */,
 +            })
 +          },
 +        }
 +      }
 +
 +      const createMultipleVaporForwardedSlots = (
 +        targetComponent: any,
 +        count: number,
 +      ) => {
 +        let current = targetComponent
 +        for (let i = 0; i < count; i++) {
 +          current = createVaporForwardedSlot(current)
 +        }
 +        return current
 +      }
 +
 +      const createMultipleVdomForwardedSlots = (
 +        targetComponent: any,
 +        count: number,
 +      ) => {
 +        let current = targetComponent
 +        for (let i = 0; i < count; i++) {
 +          current = createVdomForwardedSlot(current)
 +        }
 +        return current
 +      }
 +
 +      const createTestApp = (
 +        rootComponent: any,
 +        foo: Ref<string>,
 +        show: Ref<Boolean>,
 +      ) => {
 +        return {
 +          setup() {
 +            return () =>
 +              h(
 +                rootComponent,
 +                null,
 +                createSlots({ _: 2 /* DYNAMIC */ } as any, [
 +                  show.value
 +                    ? {
 +                        name: 'foo',
 +                        fn: () => [h('span', foo.value)],
 +                        key: '0',
 +                      }
 +                    : undefined,
 +                ]),
 +              )
 +          },
 +        }
 +      }
 +
 +      const createEmptyTestApp = (rootComponent: any) => {
 +        return {
 +          setup() {
 +            return () => h(rootComponent)
 +          },
 +        }
 +      }
 +
 +      test('vdom slot > vapor forwarded slot > vapor slot', async () => {
 +        const foo = ref('foo')
 +        const show = ref(true)
 +
 +        const VaporSlot = createVaporSlot()
 +        const VaporForwardedSlot = createVaporForwardedSlot(VaporSlot)
 +        const App = createTestApp(VaporForwardedSlot, foo, show)
 +
 +        const root = document.createElement('div')
 +        createApp(App).use(vaporInteropPlugin).mount(root)
 +        expect(root.innerHTML).toBe('<span>foo</span><!--slot-->')
 +
 +        foo.value = 'bar'
 +        await nextTick()
 +        expect(root.innerHTML).toBe('<span>bar</span><!--slot-->')
 +
 +        show.value = false
 +        await nextTick()
 +        expect(root.innerHTML).toBe('<div>fallback</div><!--slot-->')
 +      })
 +
 +      test('vdom slot > vapor forwarded slot(with fallback) > vapor slot', async () => {
 +        const foo = ref('foo')
 +        const show = ref(true)
 +
 +        const VaporSlot = createVaporSlot()
 +        const VaporForwardedSlotWithFallback = createVaporForwardedSlot(
 +          VaporSlot,
 +          'forwarded fallback',
 +        )
 +        const App = createTestApp(VaporForwardedSlotWithFallback, foo, show)
 +
 +        const root = document.createElement('div')
 +        createApp(App).use(vaporInteropPlugin).mount(root)
 +        expect(root.innerHTML).toBe('<span>foo</span><!--slot-->')
 +
 +        foo.value = 'bar'
 +        await nextTick()
 +        expect(root.innerHTML).toBe('<span>bar</span><!--slot-->')
 +
 +        show.value = false
 +        await nextTick()
 +        expect(root.innerHTML).toBe('<div>forwarded fallback</div><!--slot-->')
 +      })
 +
 +      test('vdom slot > vapor forwarded slot > vdom slot', async () => {
 +        const foo = ref('foo')
 +        const show = ref(true)
 +
 +        const VdomSlot = createVdomSlot()
 +        const VaporForwardedSlot = createVaporForwardedSlot(VdomSlot)
 +        const App = createTestApp(VaporForwardedSlot, foo, show)
 +
 +        const root = document.createElement('div')
 +        createApp(App).use(vaporInteropPlugin).mount(root)
 +        expect(root.innerHTML).toBe('<span>foo</span>')
 +
 +        foo.value = 'bar'
 +        await nextTick()
 +        expect(root.innerHTML).toBe('<span>bar</span>')
 +
 +        show.value = false
 +        await nextTick()
 +        expect(root.innerHTML).toBe('<div>fallback</div>')
 +
 +        show.value = true
 +        await nextTick()
 +        expect(root.innerHTML).toBe('<span>bar</span>')
 +      })
 +
 +      test('vdom slot > vapor forwarded slot(with fallback) > vdom slot', async () => {
 +        const foo = ref('foo')
 +        const show = ref(true)
 +
 +        const VdomSlot = createVdomSlot()
 +        const VaporForwardedSlotWithFallback = createVaporForwardedSlot(
 +          VdomSlot,
 +          'forwarded fallback',
 +        )
 +        const App = createTestApp(VaporForwardedSlotWithFallback, foo, show)
 +
 +        const root = document.createElement('div')
 +        createApp(App).use(vaporInteropPlugin).mount(root)
 +        expect(root.innerHTML).toBe('<span>foo</span>')
 +
 +        foo.value = 'bar'
 +        await nextTick()
 +        expect(root.innerHTML).toBe('<span>bar</span>')
 +
 +        show.value = false
 +        await nextTick()
 +        expect(root.innerHTML).toBe('<div>forwarded fallback</div>')
 +      })
 +
 +      test('vdom slot > vapor forwarded slot > vdom forwarded slot > vapor slot', async () => {
 +        const foo = ref('foo')
 +        const show = ref(true)
 +
 +        const VaporSlot = createVaporSlot()
 +        const VdomForwardedSlot = createVdomForwardedSlot(VaporSlot)
 +        const VaporForwardedSlot = createVaporForwardedSlot(VdomForwardedSlot)
 +        const App = createTestApp(VaporForwardedSlot, foo, show)
 +
 +        const root = document.createElement('div')
 +        createApp(App).use(vaporInteropPlugin).mount(root)
 +        expect(root.innerHTML).toBe('<span>foo</span>')
 +
 +        foo.value = 'bar'
 +        await nextTick()
 +        expect(root.innerHTML).toBe('<span>bar</span>')
 +
 +        show.value = false
 +        await nextTick()
 +        expect(root.innerHTML).toBe('<div>fallback</div>')
 +
 +        show.value = true
 +        await nextTick()
 +        expect(root.innerHTML).toBe('<span>bar</span>')
 +      })
 +
 +      test('vdom slot > vapor forwarded slot(with fallback) > vdom forwarded slot > vapor slot', async () => {
 +        const foo = ref('foo')
 +        const show = ref(true)
 +
 +        const VaporSlot = createVaporSlot()
 +        const VdomForwardedSlot = createVdomForwardedSlot(VaporSlot)
 +        const VaporForwardedSlotWithFallback = createVaporForwardedSlot(
 +          VdomForwardedSlot,
 +          'forwarded fallback',
 +        )
 +        const App = createTestApp(VaporForwardedSlotWithFallback, foo, show)
 +
 +        const root = document.createElement('div')
 +        createApp(App).use(vaporInteropPlugin).mount(root)
 +        expect(root.innerHTML).toBe('<span>foo</span>')
 +
 +        foo.value = 'bar'
 +        await nextTick()
 +        expect(root.innerHTML).toBe('<span>bar</span>')
 +
 +        show.value = false
 +        await nextTick()
 +        expect(root.innerHTML).toBe('<div>forwarded fallback</div>')
 +
 +        show.value = true
 +        await nextTick()
 +        expect(root.innerHTML).toBe('<span>bar</span>')
 +      })
 +
 +      test('vdom slot > vapor forwarded slot > vdom forwarded slot(with fallback) > vapor slot', async () => {
 +        const foo = ref('foo')
 +        const show = ref(true)
 +
 +        const VaporSlot = createVaporSlot()
 +        const VdomForwardedSlotWithFallback = createVdomForwardedSlot(
 +          VaporSlot,
 +          'vdom fallback',
 +        )
 +        const VaporForwardedSlot = createVaporForwardedSlot(
 +          VdomForwardedSlotWithFallback,
 +        )
 +        const App = createTestApp(VaporForwardedSlot, foo, show)
 +
 +        const root = document.createElement('div')
 +        createApp(App).use(vaporInteropPlugin).mount(root)
 +        expect(root.innerHTML).toBe('<span>foo</span>')
 +
 +        foo.value = 'bar'
 +        await nextTick()
 +        expect(root.innerHTML).toBe('<span>bar</span>')
 +
 +        show.value = false
 +        await nextTick()
 +        expect(root.innerHTML).toBe('<div>vdom fallback</div>')
 +
 +        show.value = true
 +        await nextTick()
 +        expect(root.innerHTML).toBe('<span>bar</span>')
 +      })
 +
 +      test('vdom slot(empty) > vapor forwarded slot > vdom forwarded slot(with fallback) > vapor slot', async () => {
 +        const VaporSlot = createVaporSlot()
 +        const VdomForwardedSlotWithFallback = createVdomForwardedSlot(
 +          VaporSlot,
 +          'vdom fallback',
 +        )
 +        const VaporForwardedSlot = createVaporForwardedSlot(
 +          VdomForwardedSlotWithFallback,
 +        )
 +        const App = createEmptyTestApp(VaporForwardedSlot)
 +
 +        const root = document.createElement('div')
 +        createApp(App).use(vaporInteropPlugin).mount(root)
 +        expect(root.innerHTML).toBe('<div>vdom fallback</div>')
 +      })
 +
 +      test('vdom slot > vapor forwarded slot > vdom forwarded slot > vdom slot', async () => {
 +        const foo = ref('foo')
 +        const show = ref(true)
 +
 +        const VdomSlot = createVdomSlot()
 +        const VdomForwardedSlot = createVdomForwardedSlot(VdomSlot)
 +        const VaporForwardedSlot = createVaporForwardedSlot(VdomForwardedSlot)
 +        const App = createTestApp(VaporForwardedSlot, foo, show)
 +
 +        const root = document.createElement('div')
 +        createApp(App).use(vaporInteropPlugin).mount(root)
 +        expect(root.innerHTML).toBe('<span>foo</span>')
 +
 +        foo.value = 'bar'
 +        await nextTick()
 +        expect(root.innerHTML).toBe('<span>bar</span>')
 +
 +        show.value = false
 +        await nextTick()
 +        expect(root.innerHTML).toBe('<div>fallback</div>')
 +
 +        show.value = true
 +        await nextTick()
 +        expect(root.innerHTML).toBe('<span>bar</span>')
 +      })
 +
 +      test('vdom slot > vapor forwarded slot(with fallback) > vdom forwarded slot > vdom slot', async () => {
 +        const foo = ref('foo')
 +        const show = ref(true)
 +
 +        const VdomSlot = createVdomSlot()
 +        const VdomForwardedSlot = createVdomForwardedSlot(VdomSlot)
 +        const VaporForwardedSlotWithFallback = createVaporForwardedSlot(
 +          VdomForwardedSlot,
 +          'vapor fallback',
 +        )
 +        const App = createTestApp(VaporForwardedSlotWithFallback, foo, show)
 +
 +        const root = document.createElement('div')
 +        createApp(App).use(vaporInteropPlugin).mount(root)
 +        expect(root.innerHTML).toBe('<span>foo</span>')
 +
 +        foo.value = 'bar'
 +        await nextTick()
 +        expect(root.innerHTML).toBe('<span>bar</span>')
 +
 +        show.value = false
 +        await nextTick()
 +        expect(root.innerHTML).toBe('<div>vapor fallback</div>')
 +
 +        show.value = true
 +        await nextTick()
 +        expect(root.innerHTML).toBe('<span>bar</span>')
 +      })
 +
 +      test('vdom slot > vapor forwarded slot > vdom forwarded slot(with fallback) > vdom slot', async () => {
 +        const foo = ref('foo')
 +        const show = ref(true)
 +
 +        const VdomSlot = createVdomSlot()
 +
 +        const VdomForwardedSlotWithFallback = createVdomForwardedSlot(
 +          VdomSlot,
 +          'vdom fallback',
 +        )
 +        const VaporForwardedSlot = createVaporForwardedSlot(
 +          VdomForwardedSlotWithFallback,
 +        )
 +        const App = createTestApp(VaporForwardedSlot, foo, show)
 +
 +        const root = document.createElement('div')
 +        createApp(App).use(vaporInteropPlugin).mount(root)
 +        expect(root.innerHTML).toBe('<span>foo</span>')
 +
 +        foo.value = 'bar'
 +        await nextTick()
 +        expect(root.innerHTML).toBe('<span>bar</span>')
 +
 +        show.value = false
 +        await nextTick()
 +        expect(root.innerHTML).toBe('<div>vdom fallback</div>')
 +
 +        show.value = true
 +        await nextTick()
 +        expect(root.innerHTML).toBe('<span>bar</span>')
 +      })
 +
 +      test('vdom slot > vapor forwarded slot (multiple) > vdom forwarded slot > vdom slot', async () => {
 +        const foo = ref('foo')
 +        const show = ref(true)
 +
 +        const VdomSlot = createVdomSlot()
 +        const VdomForwardedSlot = createVdomForwardedSlot(VdomSlot)
 +        const VaporForwardedSlot = createMultipleVaporForwardedSlots(
 +          VdomForwardedSlot,
 +          3,
 +        )
 +        const App = createTestApp(VaporForwardedSlot, foo, show)
 +
 +        const root = document.createElement('div')
 +        createApp(App).use(vaporInteropPlugin).mount(root)
 +        expect(root.innerHTML).toBe('<span>foo</span><!--slot--><!--slot-->')
 +
 +        foo.value = 'bar'
 +        await nextTick()
 +        expect(root.innerHTML).toBe('<span>bar</span><!--slot--><!--slot-->')
 +
 +        show.value = false
 +        await nextTick()
 +        expect(root.innerHTML).toBe('<div>fallback</div><!--slot--><!--slot-->')
 +
 +        show.value = true
 +        await nextTick()
 +        expect(root.innerHTML).toBe('<span>bar</span><!--slot--><!--slot-->')
 +      })
 +
 +      test('vdom slot > vapor forwarded slot (multiple) > vdom forwarded slot(with fallback) > vdom slot', async () => {
 +        const foo = ref('foo')
 +        const show = ref(true)
 +
 +        const VdomSlot = createVdomSlot()
 +        const VdomForwardedSlotWithFallback = createVdomForwardedSlot(
 +          VdomSlot,
 +          'vdom fallback',
 +        )
 +        const VaporForwardedSlot = createMultipleVaporForwardedSlots(
 +          VdomForwardedSlotWithFallback,
 +          3,
 +        )
 +        const App = createTestApp(VaporForwardedSlot, foo, show)
 +
 +        const root = document.createElement('div')
 +        createApp(App).use(vaporInteropPlugin).mount(root)
 +        expect(root.innerHTML).toBe('<span>foo</span><!--slot--><!--slot-->')
 +
 +        foo.value = 'bar'
 +        await nextTick()
 +        expect(root.innerHTML).toBe('<span>bar</span><!--slot--><!--slot-->')
 +
 +        show.value = false
 +        await nextTick()
 +        expect(root.innerHTML).toBe(
 +          '<div>vdom fallback</div><!--slot--><!--slot-->',
 +        )
 +
 +        show.value = true
 +        await nextTick()
 +        expect(root.innerHTML).toBe('<span>bar</span><!--slot--><!--slot-->')
 +      })
 +
 +      test('vdom slot > vdom forwarded slot > vapor slot', async () => {
 +        const foo = ref('foo')
 +        const show = ref(true)
 +
 +        const VaporSlot = createVaporSlot()
 +        const VdomForwardedSlot = createVdomForwardedSlot(VaporSlot)
 +        const App = createTestApp(VdomForwardedSlot, foo, show)
 +
 +        const root = document.createElement('div')
 +        createApp(App).use(vaporInteropPlugin).mount(root)
 +        expect(root.innerHTML).toBe('<span>foo</span>')
 +
 +        foo.value = 'bar'
 +        await nextTick()
 +        expect(root.innerHTML).toBe('<span>bar</span>')
 +
 +        show.value = false
 +        await nextTick()
 +        expect(root.innerHTML).toBe('<div>fallback</div>')
 +
 +        show.value = true
 +        await nextTick()
 +        expect(root.innerHTML).toBe('<span>bar</span>')
 +      })
 +
 +      test('vdom slot > vdom forwarded slot > vapor forwarded slot > vapor slot', async () => {
 +        const foo = ref('foo')
 +        const show = ref(true)
 +
 +        const VaporSlot = createVaporSlot()
 +        const VaporForwardedSlot = createVaporForwardedSlot(VaporSlot)
 +        const VdomForwardedSlot = createVdomForwardedSlot(VaporForwardedSlot)
 +        const App = createTestApp(VdomForwardedSlot, foo, show)
 +
 +        const root = document.createElement('div')
 +        createApp(App).use(vaporInteropPlugin).mount(root)
 +        expect(root.innerHTML).toBe('<span>foo</span><!--slot-->')
 +
 +        foo.value = 'bar'
 +        await nextTick()
 +        expect(root.innerHTML).toBe('<span>bar</span><!--slot-->')
 +
 +        show.value = false
 +        await nextTick()
 +        expect(root.innerHTML).toBe('<div>fallback</div><!--slot-->')
 +
 +        show.value = true
 +        await nextTick()
 +        expect(root.innerHTML).toBe('<span>bar</span><!--slot-->')
 +      })
 +
 +      test('vdom slot > vdom forwarded slot (multiple) > vapor forwarded slot > vdom slot', async () => {
 +        const foo = ref('foo')
 +        const show = ref(true)
 +
 +        const VaporSlot = createVaporSlot()
 +        const VaporForwardedSlot = createVaporForwardedSlot(VaporSlot)
 +        const VdomForwardedSlot = createMultipleVdomForwardedSlots(
 +          VaporForwardedSlot,
 +          3,
 +        )
 +        const App = createTestApp(VdomForwardedSlot, foo, show)
 +
 +        const root = document.createElement('div')
 +        createApp(App).use(vaporInteropPlugin).mount(root)
 +        expect(root.innerHTML).toBe('<span>foo</span><!--slot-->')
 +
 +        foo.value = 'bar'
 +        await nextTick()
 +        expect(root.innerHTML).toBe('<span>bar</span><!--slot-->')
 +
 +        show.value = false
 +        await nextTick()
 +        expect(root.innerHTML).toBe('<div>fallback</div><!--slot-->')
 +
 +        show.value = true
 +        await nextTick()
 +        expect(root.innerHTML).toBe('<span>bar</span><!--slot-->')
 +      })
 +
 +      test('vdom slot > vdom forwarded slot (multiple) > vapor forwarded slot(with fallback) > vdom slot', async () => {
 +        const foo = ref('foo')
 +        const show = ref(true)
 +
 +        const VaporSlot = createVaporSlot()
 +        const VaporForwardedSlot = createVaporForwardedSlot(
 +          VaporSlot,
 +          'vapor fallback',
 +        )
 +        const VdomForwardedSlot = createMultipleVdomForwardedSlots(
 +          VaporForwardedSlot,
 +          3,
 +        )
 +        const App = createTestApp(VdomForwardedSlot, foo, show)
 +
 +        const root = document.createElement('div')
 +        createApp(App).use(vaporInteropPlugin).mount(root)
 +        expect(root.innerHTML).toBe('<span>foo</span><!--slot-->')
 +
 +        foo.value = 'bar'
 +        await nextTick()
 +        expect(root.innerHTML).toBe('<span>bar</span><!--slot-->')
 +
 +        show.value = false
 +        await nextTick()
 +        expect(root.innerHTML).toBe('<div>vapor fallback</div><!--slot-->')
 +
 +        show.value = true
 +        await nextTick()
 +        expect(root.innerHTML).toBe('<span>bar</span><!--slot-->')
 +      })
 +
 +      test('vdom slot > vapor forwarded slot > vapor forwarded slot > vdom slot', async () => {
 +        const foo = ref('foo')
 +        const show = ref(true)
 +
 +        const VdomSlot = createVdomSlot()
 +        const VaporForwardedSlot1 = createMultipleVaporForwardedSlots(
 +          VdomSlot,
 +          2,
 +        )
 +        const App = createTestApp(VaporForwardedSlot1, foo, show)
 +
 +        const root = document.createElement('div')
 +        createApp(App).use(vaporInteropPlugin).mount(root)
 +        expect(root.innerHTML).toBe('<span>foo</span><!--slot-->')
 +
 +        foo.value = 'bar'
 +        await nextTick()
 +        expect(root.innerHTML).toBe('<span>bar</span><!--slot-->')
 +
 +        show.value = false
 +        await nextTick()
 +        expect(root.innerHTML).toBe('<div>fallback</div><!--slot-->')
 +
 +        show.value = true
 +        await nextTick()
 +        expect(root.innerHTML).toBe('<span>bar</span><!--slot-->')
 +      })
 +
 +      test('vdom slot > vapor forwarded slot(with fallback) > vapor forwarded slot > vdom slot', async () => {
 +        const foo = ref('foo')
 +        const show = ref(true)
 +
 +        const VdomSlot = createVdomSlot()
 +        const VaporForwardedSlot2 = createVaporForwardedSlot(VdomSlot)
 +        const VaporForwardedSlot1WithFallback = createVaporForwardedSlot(
 +          VaporForwardedSlot2,
 +          'vapor1 fallback',
 +        )
 +        const App = createTestApp(VaporForwardedSlot1WithFallback, foo, show)
 +
 +        const root = document.createElement('div')
 +        createApp(App).use(vaporInteropPlugin).mount(root)
 +        expect(root.innerHTML).toBe('<span>foo</span><!--slot-->')
 +
 +        foo.value = 'bar'
 +        await nextTick()
 +        expect(root.innerHTML).toBe('<span>bar</span><!--slot-->')
 +
 +        show.value = false
 +        await nextTick()
 +        expect(root.innerHTML).toBe('<div>vapor1 fallback</div><!--slot-->')
 +
 +        show.value = true
 +        await nextTick()
 +        expect(root.innerHTML).toBe('<span>bar</span><!--slot-->')
 +      })
 +
 +      test('vdom slot > vapor forwarded slot > vapor forwarded slot(with fallback) > vdom slot', async () => {
 +        const foo = ref('foo')
 +        const show = ref(true)
 +
 +        const VdomSlot = createVdomSlot()
 +        const VaporForwardedSlot2WithFallback = createVaporForwardedSlot(
 +          VdomSlot,
 +          'vapor2 fallback',
 +        )
 +        const VaporForwardedSlot1 = createVaporForwardedSlot(
 +          VaporForwardedSlot2WithFallback,
 +        )
 +        const App = createTestApp(VaporForwardedSlot1, foo, show)
 +
 +        const root = document.createElement('div')
 +        createApp(App).use(vaporInteropPlugin).mount(root)
 +        expect(root.innerHTML).toBe('<span>foo</span><!--slot-->')
 +
 +        foo.value = 'bar'
 +        await nextTick()
 +        expect(root.innerHTML).toBe('<span>bar</span><!--slot-->')
 +
 +        show.value = false
 +        await nextTick()
 +        expect(root.innerHTML).toBe('<div>vapor2 fallback</div><!--slot-->')
 +
 +        show.value = true
 +        await nextTick()
 +        expect(root.innerHTML).toBe('<span>bar</span><!--slot-->')
 +      })
 +
 +      test('vdom slot > vapor forwarded slot > vapor forwarded slot > vapor slot', async () => {
 +        const foo = ref('foo')
 +        const show = ref(true)
 +
 +        const VaporSlot = createVaporSlot()
 +        const VaporForwardedSlot2 = createVaporForwardedSlot(VaporSlot)
 +        const VaporForwardedSlot1 =
 +          createVaporForwardedSlot(VaporForwardedSlot2)
 +        const App = createTestApp(VaporForwardedSlot1, foo, show)
 +
 +        const root = document.createElement('div')
 +        createApp(App).use(vaporInteropPlugin).mount(root)
 +        expect(root.innerHTML).toBe('<span>foo</span><!--slot--><!--slot-->')
 +
 +        foo.value = 'bar'
 +        await nextTick()
 +        expect(root.innerHTML).toBe('<span>bar</span><!--slot--><!--slot-->')
 +
 +        show.value = false
 +        await nextTick()
 +        expect(root.innerHTML).toBe('<div>fallback</div><!--slot--><!--slot-->')
 +
 +        show.value = true
 +        await nextTick()
 +        expect(root.innerHTML).toBe('<span>bar</span><!--slot--><!--slot-->')
 +      })
 +
 +      test('vdom slot > vapor forwarded slot(with fallback) > vapor forwarded slot(with fallback) > vdom slot', async () => {
 +        const foo = ref('foo')
 +        const show = ref(true)
 +
 +        const VdomSlot = createVdomSlot()
 +        const VaporForwardedSlot2WithFallback = createVaporForwardedSlot(
 +          VdomSlot,
 +          'vapor2 fallback',
 +        )
 +        const VaporForwardedSlot1WithFallback = createVaporForwardedSlot(
 +          VaporForwardedSlot2WithFallback,
 +          'vapor1 fallback',
 +        )
 +        const App = createTestApp(VaporForwardedSlot1WithFallback, foo, show)
 +
 +        const root = document.createElement('div')
 +        createApp(App).use(vaporInteropPlugin).mount(root)
 +        expect(root.innerHTML).toBe('<span>foo</span><!--slot-->')
 +
 +        foo.value = 'bar'
 +        await nextTick()
 +        expect(root.innerHTML).toBe('<span>bar</span><!--slot-->')
 +
 +        show.value = false
 +        await nextTick()
 +        expect(root.innerHTML).toBe('<div>vapor1 fallback</div><!--slot-->')
 +
 +        show.value = true
 +        await nextTick()
 +        expect(root.innerHTML).toBe('<span>bar</span><!--slot-->')
 +      })
 +
 +      test('vdom slot > vapor forwarded slot(with fallback) > vapor forwarded slot(with fallback) > vapor slot', async () => {
 +        const foo = ref('foo')
 +        const show = ref(true)
 +
 +        const VaporSlot = createVaporSlot()
 +        const VaporForwardedSlot2WithFallback = createVaporForwardedSlot(
 +          VaporSlot,
 +          'vapor2 fallback',
 +        )
 +        const VaporForwardedSlot1WithFallback = createVaporForwardedSlot(
 +          VaporForwardedSlot2WithFallback,
 +          'vapor1 fallback',
 +        )
 +        const App = createTestApp(VaporForwardedSlot1WithFallback, foo, show)
 +
 +        const root = document.createElement('div')
 +        createApp(App).use(vaporInteropPlugin).mount(root)
 +        expect(root.innerHTML).toBe('<span>foo</span><!--slot--><!--slot-->')
 +
 +        foo.value = 'bar'
 +        await nextTick()
 +        expect(root.innerHTML).toBe('<span>bar</span><!--slot--><!--slot-->')
 +
 +        show.value = false
 +        await nextTick()
 +        expect(root.innerHTML).toBe(
 +          '<div>vapor1 fallback</div><!--slot--><!--slot-->',
 +        )
 +
 +        show.value = true
 +        await nextTick()
 +        expect(root.innerHTML).toBe('<span>bar</span><!--slot--><!--slot-->')
 +      })
 +
 +      test('vdom slot > vdom forwarded slot(with fallback) > vdom forwarded slot(with fallback) > vapor slot', async () => {
 +        const foo = ref('foo')
 +        const show = ref(true)
 +
 +        const VaporSlot = createVaporSlot()
 +        const VdomForwardedSlot2WithFallback = createVdomForwardedSlot(
 +          VaporSlot,
 +          'vdom2 fallback',
 +        )
 +        const VdomForwardedSlot1WithFallback = createVdomForwardedSlot(
 +          VdomForwardedSlot2WithFallback,
 +          'vdom1 fallback',
 +        )
 +        const App = createTestApp(VdomForwardedSlot1WithFallback, foo, show)
 +
 +        const root = document.createElement('div')
 +        createApp(App).use(vaporInteropPlugin).mount(root)
 +        expect(root.innerHTML).toBe('<span>foo</span>')
 +
 +        foo.value = 'bar'
 +        await nextTick()
 +        expect(root.innerHTML).toBe('<span>bar</span>')
 +
 +        show.value = false
 +        await nextTick()
 +        expect(root.innerHTML).toBe('<div>vdom1 fallback</div>')
 +
 +        show.value = true
 +        await nextTick()
 +        expect(root.innerHTML).toBe('<span>bar</span>')
 +      })
 +
 +      test('vdom slot > vdom forwarded slot(with fallback) > vdom forwarded slot(with fallback) > vdom slot', async () => {
 +        const foo = ref('foo')
 +        const show = ref(true)
 +
 +        const VdomSlot = createVdomSlot()
 +        const VdomForwardedSlot2WithFallback = createVdomForwardedSlot(
 +          VdomSlot,
 +          'vdom2 fallback',
 +        )
 +        const VdomForwardedSlot1WithFallback = createVdomForwardedSlot(
 +          VdomForwardedSlot2WithFallback,
 +          'vdom1 fallback',
 +        )
 +        const App = createTestApp(VdomForwardedSlot1WithFallback, foo, show)
 +
 +        const root = document.createElement('div')
 +        createApp(App).use(vaporInteropPlugin).mount(root)
 +        expect(root.innerHTML).toBe('<span>foo</span>')
 +
 +        foo.value = 'bar'
 +        await nextTick()
 +        expect(root.innerHTML).toBe('<span>bar</span>')
 +
 +        show.value = false
 +        await nextTick()
 +        expect(root.innerHTML).toBe('<div>vdom1 fallback</div>')
 +
 +        show.value = true
 +        await nextTick()
 +        expect(root.innerHTML).toBe('<span>bar</span>')
 +      })
 +
 +      test('vdom slot > vdom forwarded slot(with fallback) > vdom forwarded slot(with fallback) (multiple) > vapor slot', async () => {
 +        const foo = ref('foo')
 +        const show = ref(true)
 +
 +        const VaporSlot = createVaporSlot()
 +        const VdomForwardedSlot3WithFallback = createVdomForwardedSlot(
 +          VaporSlot,
 +          'vdom3 fallback',
 +        )
 +        const VdomForwardedSlot2WithFallback = createVdomForwardedSlot(
 +          VdomForwardedSlot3WithFallback,
 +          'vdom2 fallback',
 +        )
 +        const VdomForwardedSlot1WithFallback = createVdomForwardedSlot(
 +          VdomForwardedSlot2WithFallback,
 +          'vdom1 fallback',
 +        )
 +        const App = createTestApp(VdomForwardedSlot1WithFallback, foo, show)
 +
 +        const root = document.createElement('div')
 +        createApp(App).use(vaporInteropPlugin).mount(root)
 +        expect(root.innerHTML).toBe('<span>foo</span>')
 +
 +        foo.value = 'bar'
 +        await nextTick()
 +        expect(root.innerHTML).toBe('<span>bar</span>')
 +
 +        show.value = false
 +        await nextTick()
 +        expect(root.innerHTML).toBe('<div>vdom1 fallback</div>')
 +
 +        show.value = true
 +        await nextTick()
 +        expect(root.innerHTML).toBe('<span>bar</span>')
 +      })
 +    })
 +  })
  })
index ed9143e436e17c3765c5fb7ec7526e5afcba48e0,8f43e86529090826995c4512088231dee7f07a03..8ac11c9817174704962df545c271099d9680af4d
@@@ -41,7 -42,7 +43,8 @@@ export function createDynamicComponent
            rawSlots,
            isSingleRoot,
            once,
 +          scopeId,
+           appContext,
          ),
        value,
      )
index 0000000000000000000000000000000000000000,dd6143950e349daf74e3d91e86829752bf9c8083..263986e16b4ecb4da4528e3dde9996a893876cdf
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,202 +1,203 @@@
+ import {
+   type AsyncComponentLoader,
+   type AsyncComponentOptions,
+   ErrorCodes,
+   createAsyncComponentContext,
+   currentInstance,
+   handleError,
+   markAsyncBoundary,
+   performAsyncHydrate,
+   useAsyncComponentState,
+   watch,
+ } from '@vue/runtime-dom'
+ import { defineVaporComponent } from './apiDefineComponent'
+ import {
+   type VaporComponent,
+   type VaporComponentInstance,
+   createComponent,
+ } from './component'
+ import { renderEffect } from './renderEffect'
+ import { DynamicFragment } from './fragment'
+ import {
+   hydrateNode,
+   isComment,
+   isHydrating,
+   locateEndAnchor,
+   removeFragmentNodes,
+ } from './dom/hydration'
+ import { invokeArrayFns } from '@vue/shared'
+ import { insert, remove } from './block'
+ import { parentNode } from './dom/node'
+ /*@ __NO_SIDE_EFFECTS__ */
+ export function defineVaporAsyncComponent<T extends VaporComponent>(
+   source: AsyncComponentLoader<T> | AsyncComponentOptions<T>,
+ ): T {
+   const {
+     load,
+     getResolvedComp,
+     setPendingRequest,
+     source: {
+       loadingComponent,
+       errorComponent,
+       delay,
+       hydrate: hydrateStrategy,
+       timeout,
+       suspensible = true,
+     },
+   } = createAsyncComponentContext<T, VaporComponent>(source)
+   return defineVaporComponent({
+     name: 'VaporAsyncComponentWrapper',
+     __asyncLoader: load,
+     __asyncHydrate(
+       el: Element,
+       instance: VaporComponentInstance,
+       // Note: this hydrate function essentially calls the setup method of the component
+       // not the actual hydrate function
+       hydrate: () => void,
+     ) {
+       // if async component needs to be updated before hydration, hydration is no longer needed.
+       let isHydrated = false
+       watch(
+         () => instance.attrs,
+         () => {
+           // early return if already hydrated
+           if (isHydrated) return
+           // call the beforeUpdate hook to avoid calling hydrate in performAsyncHydrate
+           instance.bu && invokeArrayFns(instance.bu)
+           // mount the inner component and remove the placeholder
+           const parent = parentNode(el)!
+           load().then(() => {
+             if (instance.isUnmounted) return
+             hydrate()
+             if (isComment(el, '[')) {
+               const endAnchor = locateEndAnchor(el)!
+               removeFragmentNodes(el, endAnchor)
+               insert(instance.block, parent, endAnchor)
+             } else {
+               insert(instance.block, parent, el)
+               remove(el, parent)
+             }
+           })
+         },
+         { deep: true, once: true },
+       )
+       performAsyncHydrate(
+         el,
+         instance,
+         () => {
+           hydrateNode(el, () => {
+             hydrate()
+             insert(instance.block, parentNode(el)!, el)
+             isHydrated = true
+           })
+         },
+         getResolvedComp,
+         load,
+         hydrateStrategy,
+       )
+     },
+     get __asyncResolved() {
+       return getResolvedComp()
+     },
+     setup() {
+       const instance = currentInstance as VaporComponentInstance
+       markAsyncBoundary(instance)
+       const frag =
+         __DEV__ || isHydrating
+           ? new DynamicFragment('async component')
+           : new DynamicFragment()
+       // already resolved
+       let resolvedComp = getResolvedComp()
+       if (resolvedComp) {
+         frag!.update(() => createInnerComp(resolvedComp!, instance))
+         return frag
+       }
+       const onError = (err: Error) => {
+         setPendingRequest(null)
+         handleError(
+           err,
+           instance,
+           ErrorCodes.ASYNC_COMPONENT_LOADER,
+           !errorComponent /* do not throw in dev if user provided error component */,
+         )
+       }
+       // TODO suspense-controlled
+       if (__FEATURE_SUSPENSE__ && suspensible && instance.suspense) {
+       }
+       const { loaded, error, delayed } = useAsyncComponentState(
+         delay,
+         timeout,
+         onError,
+       )
+       load()
+         .then(() => {
+           loaded.value = true
+           // TODO parent is keep-alive, force update so the loaded component's
+           // name is taken into account
+         })
+         .catch(err => {
+           onError(err)
+           error.value = err
+         })
+       renderEffect(() => {
+         resolvedComp = getResolvedComp()
+         let render
+         if (loaded.value && resolvedComp) {
+           render = () => createInnerComp(resolvedComp!, instance, frag)
+         } else if (error.value && errorComponent) {
+           render = () =>
+             createComponent(errorComponent, { error: () => error.value })
+         } else if (loadingComponent && !delayed.value) {
+           render = () => createComponent(loadingComponent)
+         }
+         frag!.update(render)
+       })
+       return frag
+     },
+   }) as T
+ }
+ function createInnerComp(
+   comp: VaporComponent,
+   parent: VaporComponentInstance,
+   frag?: DynamicFragment,
+ ): VaporComponentInstance {
+   const { rawProps, rawSlots, isSingleRoot, appContext } = parent
+   const instance = createComponent(
+     comp,
+     rawProps,
+     rawSlots,
+     isSingleRoot,
+     undefined,
++    undefined,
+     appContext,
+   )
+   // set ref
+   // @ts-expect-error
+   frag && frag.setRef && frag.setRef(instance)
+   // TODO custom element
+   // pass the custom element callback on to the inner comp
+   // and remove it from the async wrapper
+   // i.ce = ce
+   // delete parent.ce
+   return instance
+ }
index 6bbc08cc6e56589fe83557c2d7efc6520227db28,c4c2f0e188a2f6693cedf78a48523f290aa138c3..aad5840f10ee6586fa7e33a423cc6dcd40ace1bc
@@@ -5,88 -5,43 +5,44 @@@ import 
    mountComponent,
    unmountComponent,
  } from './component'
- import { createComment, createTextNode } from './dom/node'
- import { EffectScope, pauseTracking, resetTracking } from '@vue/reactivity'
- import { isHydrating } from './dom/hydration'
- import { getInheritedScopeIds } from '@vue/runtime-dom'
- export type Block =
-   | Node
-   | VaporFragment
-   | DynamicFragment
-   | VaporComponentInstance
-   | Block[]
- export type BlockFn = (...args: any[]) => Block
- export class VaporFragment {
-   nodes: Block
-   anchor?: Node
-   insert?: (parent: ParentNode, anchor: Node | null) => void
-   remove?: (parent?: ParentNode) => void
-   fallback?: BlockFn
+ import { _child } from './dom/node'
+ import { isComment, isHydrating } from './dom/hydration'
+ import {
+   type TransitionHooks,
+   type TransitionProps,
+   type TransitionState,
++  getInheritedScopeIds,
+   performTransitionEnter,
+   performTransitionLeave,
+ } from '@vue/runtime-dom'
+ import {
+   type DynamicFragment,
+   type VaporFragment,
+   isFragment,
+ } from './fragment'
+ import { TeleportFragment } from './components/Teleport'
  
-   constructor(nodes: Block) {
-     this.nodes = nodes
-   }
+ export interface VaporTransitionHooks extends TransitionHooks {
+   state: TransitionState
+   props: TransitionProps
+   instance: VaporComponentInstance
+   // mark transition hooks as disabled so that it skips during
+   // inserting
+   disabled?: boolean
  }
  
- export class DynamicFragment extends VaporFragment {
-   anchor: Node
-   scope: EffectScope | undefined
-   current?: BlockFn
-   fallback?: BlockFn
-   /**
-    * slot only
-    * indicates forwarded slot
-    */
-   forwarded?: boolean
-   constructor(anchorLabel?: string) {
-     super([])
-     this.anchor =
-       __DEV__ && anchorLabel ? createComment(anchorLabel) : createTextNode()
-   }
-   update(render?: BlockFn, key: any = render): void {
-     if (key === this.current) {
-       return
-     }
-     this.current = key
-     pauseTracking()
-     const parent = this.anchor.parentNode
-     // teardown previous branch
-     if (this.scope) {
-       this.scope.stop()
-       parent && remove(this.nodes, parent)
-     }
-     if (render) {
-       this.scope = new EffectScope()
-       this.nodes = this.scope.run(render) || []
-       if (parent) insert(this.nodes, parent, this.anchor)
-     } else {
-       this.scope = undefined
-       this.nodes = []
-     }
-     if (this.fallback && !isValidBlock(this.nodes)) {
-       parent && remove(this.nodes, parent)
-       this.nodes =
-         (this.scope || (this.scope = new EffectScope())).run(this.fallback) ||
-         []
-       parent && insert(this.nodes, parent, this.anchor)
-     }
-     resetTracking()
-   }
+ export interface TransitionOptions {
+   $key?: any
+   $transition?: VaporTransitionHooks
  }
  
- export function isFragment(val: NonNullable<unknown>): val is VaporFragment {
-   return val instanceof VaporFragment
- }
+ export type TransitionBlock =
+   | (Node & TransitionOptions)
+   | (VaporFragment & TransitionOptions)
+   | (DynamicFragment & TransitionOptions)
+ export type Block = TransitionBlock | VaporComponentInstance | Block[]
+ export type BlockFn = (...args: any[]) => Block
  
  export function isBlock(val: NonNullable<unknown>): val is Block {
    return (
@@@ -198,40 -179,44 +180,81 @@@ export function normalizeBlock(block: B
    return nodes
  }
  
 +export function setScopeId(block: Block, scopeId: string): void {
 +  if (block instanceof Element) {
 +    block.setAttribute(scopeId, '')
 +  } else if (isVaporComponent(block)) {
 +    setScopeId(block.block, scopeId)
 +  } else if (isArray(block)) {
 +    for (const b of block) {
 +      setScopeId(b, scopeId)
 +    }
 +  } else if (isFragment(block)) {
 +    setScopeId(block.nodes, scopeId)
 +  }
 +}
 +
 +export function setComponentScopeId(instance: VaporComponentInstance): void {
 +  const parent = instance.parent
 +  if (!parent) return
 +  if (isArray(instance.block) && instance.block.length > 1) return
 +
 +  const scopeId = parent.type.__scopeId
 +  if (scopeId) {
 +    setScopeId(instance.block, scopeId)
 +  }
 +
 +  // inherit scopeId from vdom parent
 +  if (
 +    parent.subTree &&
 +    (parent.subTree.component as any) === instance &&
 +    parent.vnode!.scopeId
 +  ) {
 +    setScopeId(instance.block, parent.vnode!.scopeId)
 +    const scopeIds = getInheritedScopeIds(parent.vnode!, parent.parent)
 +    for (const id of scopeIds) {
 +      setScopeId(instance.block, id)
 +    }
 +  }
 +}
+ export function findBlockNode(block: Block): {
+   parentNode: Node | null
+   nextNode: Node | null
+ } {
+   let { parentNode, nextSibling: nextNode } = findLastChild(block)!
+   // if nodes render as a fragment and the current nextNode is fragment
+   // end anchor, need to move to the next node
+   if (nextNode && isComment(nextNode, ']') && isFragmentBlock(block)) {
+     nextNode = nextNode.nextSibling
+   }
+   return {
+     parentNode,
+     nextNode,
+   }
+ }
+ function findLastChild(node: Block): Node | undefined | null {
+   if (node && node instanceof Node) {
+     return node
+   } else if (isArray(node)) {
+     return findLastChild(node[node.length - 1])
+   } else if (isVaporComponent(node)) {
+     return findLastChild(node.block!)
+   } else {
+     if (node.anchor) return node.anchor
+     return findLastChild(node.nodes!)
+   }
+ }
+ export function isFragmentBlock(block: Block): boolean {
+   if (isArray(block)) {
+     return true
+   } else if (isVaporComponent(block)) {
+     return isFragmentBlock(block.block!)
+   } else if (isFragment(block)) {
+     return isFragmentBlock(block.nodes)
+   }
+   return false
+ }
index 6e59c0bdef5c4801fa6246a8cdeffe37a5070b20,1952b31048d729bd44822de37a89f394179095dc..bafb042bc12a2d3038e79087aed995cffc2bb0f2
@@@ -25,15 -28,7 +28,14 @@@ import 
    unregisterHMR,
    warn,
  } from '@vue/runtime-dom'
 -import { type Block, insert, isBlock, remove } from './block'
 +import {
 +  type Block,
-   DynamicFragment,
 +  insert,
 +  isBlock,
 +  remove,
 +  setComponentScopeId,
 +  setScopeId,
 +} from './block'
  import {
    type ShallowRef,
    markRaw,
@@@ -146,8 -164,7 +171,8 @@@ export function createComponent
    rawProps?: LooseRawProps | null,
    rawSlots?: LooseRawSlots | null,
    isSingleRoot?: boolean,
-   once?: boolean, // TODO once support
+   once?: boolean,
 +  scopeId?: string,
    appContext: GenericAppContext = (currentInstance &&
      currentInstance.appContext) ||
      emptyContext,
@@@ -486,7 -611,7 +619,8 @@@ export function createComponentWithFall
    rawSlots?: LooseRawSlots | null,
    isSingleRoot?: boolean,
    once?: boolean,
 +  scopeId?: string,
+   appContext?: GenericAppContext,
  ): HTMLElement | VaporComponentInstance {
    if (!isString(comp)) {
      return createComponent(
        rawSlots,
        isSingleRoot,
        once,
 +      scopeId,
+       appContext,
      )
    }
  
    // mark single root
    ;(el as any).$root = isSingleRoot
  
-   scopeId = scopeId || currentInstance!.type.__scopeId
-   if (scopeId) setScopeId(el, scopeId)
++  if (!isHydrating) {
++    scopeId = scopeId || currentInstance!.type.__scopeId
++    if (scopeId) setScopeId(el, scopeId)
++  }
 +
    if (rawProps) {
-     renderEffect(() => {
+     const setFn = () =>
        setDynamicProps(el, [resolveDynamicProps(rawProps as RawProps)])
-     })
+     if (once) setFn()
+     else renderEffect(setFn)
    }
  
    if (rawSlots) {
@@@ -544,9 -688,16 +703,17 @@@ export function mountComponent
      startMeasure(instance, `mount`)
    }
    if (instance.bm) invokeArrayFns(instance.bm)
-   insert(instance.block, parent, anchor)
-   setComponentScopeId(instance)
-   if (instance.m) queuePostFlushCb(() => invokeArrayFns(instance.m!))
+   if (!isHydrating) {
+     insert(instance.block, parent, anchor)
++    setComponentScopeId(instance)
+   }
+   if (instance.m) queuePostFlushCb(instance.m!)
+   if (
+     instance.shapeFlag! & ShapeFlags.COMPONENT_SHOULD_KEEP_ALIVE &&
+     instance.a
+   ) {
+     queuePostFlushCb(instance.a!)
+   }
    instance.isMounted = true
    if (__DEV__) {
      endMeasure(instance, `mount`)
index 020c729fa30b95a19175972744c7e54d0abd3c42,aa0651658c057fdce6c02bbdd0fce7c95d8982b5..a0814e5b32d0119b3d8d6b26cf642efd7571b02d
@@@ -1,13 -1,5 +1,5 @@@
  import { EMPTY_OBJ, NO, hasOwn, isArray, isFunction } from '@vue/shared'
- import {
-   type Block,
-   type BlockFn,
-   DynamicFragment,
-   type VaporFragment,
-   insert,
-   isFragment,
-   setScopeId,
- } from './block'
 -import { type Block, type BlockFn, insert } from './block'
++import { type Block, type BlockFn, insert, setScopeId } from './block'
  import { rawPropsProxyHandlers } from './componentProps'
  import { currentInstance, isRef } from '@vue/runtime-dom'
  import type { LooseRawProps, VaporComponentInstance } from './component'
@@@ -189,36 -160,16 +160,34 @@@ export function createSlot
      }
    }
  
-   if (i || !hasForwardedSlot(fragment.nodes)) {
-     const scopeId = instance!.type.__scopeId
-     if (scopeId) setScopeId(fragment, `${scopeId}-s`)
-   }
 +  if (i) fragment.forwarded = true
-   if (!isHydrating && _insertionParent) {
-     insert(fragment, _insertionParent, _insertionAnchor)
 +
+   if (!isHydrating) {
++    if (i || !hasForwardedSlot(fragment.nodes)) {
++      const scopeId = instance!.type.__scopeId
++      if (scopeId) setScopeId(fragment, `${scopeId}-s`)
++    }
+     if (_insertionParent) insert(fragment, _insertionParent, _insertionAnchor)
+   } else {
+     if (fragment.insert) {
+       ;(fragment as VaporFragment).hydrate!()
+     }
+     if (_isLastInsertion) {
+       advanceHydrationNode(_insertionParent!)
+     }
    }
  
    return fragment
  }
- function ensureVaporSlotFallback(
-   block: VaporFragment,
-   fallback?: VaporSlot,
- ): void {
-   if (block.insert && !block.fallback && fallback) {
-     block.fallback = fallback
-   }
- }
 +
 +function isForwardedSlot(block: Block): block is DynamicFragment {
 +  return block instanceof DynamicFragment && !!block.forwarded
 +}
 +
 +function hasForwardedSlot(block: Block): block is DynamicFragment {
 +  if (isArray(block)) {
 +    return block.some(isForwardedSlot)
 +  } else {
 +    return isForwardedSlot(block)
 +  }
 +}
index 0000000000000000000000000000000000000000,07f1243e4e5076acf4b4cba41af9c5d9f8eb9440..dbd986d59eb786cbd51f8ec6156f531cf5f057f2
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,273 +1,274 @@@
+ import { EffectScope, setActiveSub } from '@vue/reactivity'
+ import { createComment, createTextNode } from './dom/node'
+ import {
+   type Block,
+   type BlockFn,
+   type TransitionOptions,
+   type VaporTransitionHooks,
+   findBlockNode,
+   insert,
+   isValidBlock,
+   remove,
+ } from './block'
+ import {
+   type GenericComponentInstance,
+   type TransitionHooks,
+   type VNode,
+   currentInstance,
+   isKeepAlive,
+   queuePostFlushCb,
+ } from '@vue/runtime-dom'
+ import type { VaporComponentInstance } from './component'
+ import type { NodeRef } from './apiTemplateRef'
+ import type { KeepAliveInstance } from './components/KeepAlive'
+ import {
+   applyTransitionHooks,
+   applyTransitionLeaveHooks,
+ } from './components/Transition'
+ import {
+   currentHydrationNode,
+   isComment,
+   isHydrating,
+   locateFragmentEndAnchor,
+   locateHydrationNode,
+ } from './dom/hydration'
+ export class VaporFragment<T extends Block = Block>
+   implements TransitionOptions
+ {
+   $key?: any
+   $transition?: VaporTransitionHooks | undefined
+   nodes: T
+   vnode?: VNode | null = null
+   anchor?: Node
+   fallback?: BlockFn
+   insert?: (
+     parent: ParentNode,
+     anchor: Node | null,
+     transitionHooks?: TransitionHooks,
+   ) => void
+   remove?: (parent?: ParentNode, transitionHooks?: TransitionHooks) => void
+   hydrate?: (...args: any[]) => void
+   setRef?: (
+     instance: VaporComponentInstance,
+     ref: NodeRef,
+     refFor: boolean,
+     refKey: string | undefined,
+   ) => void
+   constructor(nodes: T) {
+     this.nodes = nodes
+   }
+ }
+ export class ForFragment extends VaporFragment<Block[]> {
+   constructor(nodes: Block[]) {
+     super(nodes)
+   }
+ }
+ export class DynamicFragment extends VaporFragment {
+   anchor!: Node
+   scope: EffectScope | undefined
+   current?: BlockFn
+   fallback?: BlockFn
+   anchorLabel?: string
++  forwarded?: boolean
+   constructor(anchorLabel?: string) {
+     super([])
+     if (isHydrating) {
+       this.anchorLabel = anchorLabel
+       locateHydrationNode()
+     } else {
+       this.anchor =
+         __DEV__ && anchorLabel ? createComment(anchorLabel) : createTextNode()
+     }
+   }
+   update(render?: BlockFn, key: any = render): void {
+     if (key === this.current) {
+       if (isHydrating) this.hydrate(true)
+       return
+     }
+     this.current = key
+     const prevSub = setActiveSub()
+     const parent = isHydrating ? null : this.anchor.parentNode
+     const transition = this.$transition
+     const instance = currentInstance!
+     // teardown previous branch
+     if (this.scope) {
+       if (isKeepAlive(instance)) {
+         ;(instance as KeepAliveInstance).process(this.nodes)
+       } else {
+         this.scope.stop()
+       }
+       const mode = transition && transition.mode
+       if (mode) {
+         applyTransitionLeaveHooks(this.nodes, transition, () =>
+           this.render(render, instance, transition, parent),
+         )
+         parent && remove(this.nodes, parent)
+         if (mode === 'out-in') {
+           setActiveSub(prevSub)
+           return
+         }
+       } else {
+         parent && remove(this.nodes, parent)
+       }
+     }
+     this.render(render, instance, transition, parent)
+     if (this.fallback) {
+       // set fallback for nested fragments
+       const hasNestedFragment = isFragment(this.nodes)
+       if (hasNestedFragment) {
+         setFragmentFallback(this.nodes as VaporFragment, this.fallback)
+       }
+       const invalidFragment = findInvalidFragment(this)
+       if (invalidFragment) {
+         parent && remove(this.nodes, parent)
+         const scope = this.scope || (this.scope = new EffectScope())
+         scope.run(() => {
+           // for nested fragments, render invalid fragment's fallback
+           if (hasNestedFragment) {
+             renderFragmentFallback(invalidFragment)
+           } else {
+             this.nodes = this.fallback!() || []
+           }
+         })
+         parent && insert(this.nodes, parent, this.anchor)
+       }
+     }
+     setActiveSub(prevSub)
+     if (isHydrating) this.hydrate()
+   }
+   private render(
+     render: BlockFn | undefined,
+     instance: GenericComponentInstance,
+     transition: VaporTransitionHooks | undefined,
+     parent: ParentNode | null,
+   ) {
+     if (render) {
+       this.scope = new EffectScope()
+       this.nodes = this.scope.run(render) || []
+       if (isKeepAlive(instance)) {
+         ;(instance as KeepAliveInstance).process(this.nodes)
+       }
+       if (transition) {
+         this.$transition = applyTransitionHooks(this.nodes, transition)
+       }
+       if (parent) insert(this.nodes, parent, this.anchor)
+     } else {
+       this.scope = undefined
+       this.nodes = []
+     }
+   }
+   hydrate = (isEmpty = false): void => {
+     // avoid repeated hydration during fallback rendering
+     if (this.anchor) return
+     if (this.anchorLabel === 'if') {
+       // reuse the empty comment node as the anchor for empty if
+       // e.g. `<div v-if="false"></div>` -> `<!---->`
+       if (isEmpty) {
+         this.anchor = locateFragmentEndAnchor('')!
+         if (__DEV__ && !this.anchor) {
+           throw new Error(
+             'Failed to locate if anchor. this is likely a Vue internal bug.',
+           )
+         } else {
+           if (__DEV__) {
+             ;(this.anchor as Comment).data = this.anchorLabel
+           }
+           return
+         }
+       }
+     } else if (this.anchorLabel === 'slot') {
+       // reuse the empty comment node for empty slot
+       // e.g. `<slot v-if="false"></slot>`
+       if (isEmpty && isComment(currentHydrationNode!, '')) {
+         this.anchor = currentHydrationNode!
+         if (__DEV__) {
+           ;(this.anchor as Comment).data = this.anchorLabel!
+         }
+         return
+       }
+       // reuse the vdom fragment end anchor
+       this.anchor = locateFragmentEndAnchor()!
+       if (__DEV__ && !this.anchor) {
+         throw new Error(
+           'Failed to locate slot anchor. this is likely a Vue internal bug.',
+         )
+       } else {
+         return
+       }
+     }
+     const { parentNode, nextNode } = findBlockNode(this.nodes)!
+     // create an anchor
+     queuePostFlushCb(() => {
+       parentNode!.insertBefore(
+         (this.anchor = __DEV__
+           ? createComment(this.anchorLabel!)
+           : createTextNode()),
+         nextNode,
+       )
+     })
+   }
+ }
+ export function setFragmentFallback(
+   fragment: VaporFragment,
+   fallback: BlockFn,
+ ): void {
+   if (fragment.fallback) {
+     const originalFallback = fragment.fallback
+     // if the original fallback also renders invalid blocks,
+     // this ensures proper fallback chaining
+     fragment.fallback = () => {
+       const fallbackNodes = originalFallback()
+       if (isValidBlock(fallbackNodes)) {
+         return fallbackNodes
+       }
+       return fallback()
+     }
+   } else {
+     fragment.fallback = fallback
+   }
+   if (isFragment(fragment.nodes)) {
+     setFragmentFallback(fragment.nodes, fragment.fallback)
+   }
+ }
+ function renderFragmentFallback(fragment: VaporFragment): void {
+   if (fragment instanceof ForFragment) {
+     fragment.nodes[0] = [fragment.fallback!() || []] as Block[]
+   } else if (fragment instanceof DynamicFragment) {
+     fragment.update(fragment.fallback)
+   } else {
+     // vdom slots
+   }
+ }
+ function findInvalidFragment(fragment: VaporFragment): VaporFragment | null {
+   if (isValidBlock(fragment.nodes)) return null
+   return isFragment(fragment.nodes)
+     ? findInvalidFragment(fragment.nodes) || fragment
+     : fragment
+ }
+ export function isFragment(val: NonNullable<unknown>): val is VaporFragment {
+   return val instanceof VaporFragment
+ }
index 79959119e21f715bfbf41738a4e5406776daae27,d3cb8d243a81105e80db9db73df39654e5dbe619..d6bfb8cf09a98cceadc3990ff52d1b8e01d89b45
@@@ -173,13 -274,12 +274,13 @@@ function createVDOMComponent
    component: ConcreteComponent,
    rawProps?: LooseRawProps | null,
    rawSlots?: LooseRawSlots | null,
 +  scopeId?: string,
  ): VaporFragment {
    const frag = new VaporFragment([])
-   const vnode = createVNode(
+   const vnode = (frag.vnode = createVNode(
      component,
      rawProps && new Proxy(rawProps, rawPropsProxyHandlers),
-   )
+   ))
    const wrapper = new VaporComponentInstance(
      { props: component.props },
      rawProps as RawProps,
      internals.umt(vnode.component!, null, !!parentNode)
    }
  
-   frag.insert = (parentNode, anchor) => {
 +  vnode.scopeId = scopeId || parentInstance.type.__scopeId!
 +
+   frag.hydrate = () => {
+     hydrateVNode(vnode, parentInstance as any)
+     onScopeDispose(unmount, true)
+     isMounted = true
+     frag.nodes = vnode.el as any
+   }
+   frag.insert = (parentNode, anchor, transition) => {
+     if (isHydrating) return
+     if (vnode.shapeFlag & ShapeFlags.COMPONENT_KEPT_ALIVE) {
+       vdomActivate(
+         vnode,
+         parentNode,
+         anchor,
+         internals,
+         parentInstance as any,
+         null,
+         undefined,
+         false,
+       )
+       return
+     }
+     const prev = currentInstance
+     simpleSetCurrentInstance(parentInstance)
      if (!isMounted) {
+       if (transition) setVNodeTransitionHooks(vnode, transition)
        internals.mt(
          vnode,
          parentNode,
        )
      }
  
-     frag.nodes = vnode.el as Block
 +    // update the fragment nodes
+     frag.nodes = vnode.el as any
+     simpleSetCurrentInstance(prev)
    }
  
    frag.remove = unmount