diff --git a/packages/compiler-ssr/__tests__/ssrComponent.spec.ts b/packages/compiler-ssr/__tests__/ssrComponent.spec.ts
index 2fde4560ec4..fb2fff86574 100644
--- a/packages/compiler-ssr/__tests__/ssrComponent.spec.ts
+++ b/packages/compiler-ssr/__tests__/ssrComponent.spec.ts
@@ -39,6 +39,7 @@ describe('ssr: components', () => {
 
         return function ssrRender(_ctx, _push, _parent, _attrs) {
           _ssrRenderVNode(_push, _createVNode(_resolveDynamicComponent("foo"), _mergeProps({ prop: "b" }, _attrs), null), _parent)
+          _push(\`<!--dynamic-component-->\`)
         }"
       `)
 
@@ -49,6 +50,7 @@ describe('ssr: components', () => {
 
         return function ssrRender(_ctx, _push, _parent, _attrs) {
           _ssrRenderVNode(_push, _createVNode(_resolveDynamicComponent(_ctx.foo), _mergeProps({ prop: "b" }, _attrs), null), _parent)
+          _push(\`<!--dynamic-component-->\`)
         }"
       `)
   })
@@ -244,7 +246,8 @@ describe('ssr: components', () => {
                   _ssrRenderList(list, (i) => {
                     _push(\`<span\${_scopeId}></span>\`)
                   })
-                  _push(\`<!--]--></div>\`)
+                  _push(\`<!--]--><!--for--></div>\`)
+                  _push(\`<!--if-->\`)
                 } else {
                   _push(\`<!---->\`)
                 }
@@ -267,7 +270,8 @@ describe('ssr: components', () => {
                   _ssrRenderList(_ctx.list, (i) => {
                     _push(\`<span\${_scopeId}></span>\`)
                   })
-                  _push(\`<!--]--></div>\`)
+                  _push(\`<!--]--><!--for--></div>\`)
+                  _push(\`<!--if-->\`)
                 } else {
                   _push(\`<!---->\`)
                 }
@@ -361,6 +365,7 @@ describe('ssr: components', () => {
                   _push(\`\`)
                   if (false) {
                     _push(\`<div\${_scopeId}></div>\`)
+                    _push(\`<!--if-->\`)
                   } else {
                     _push(\`<!---->\`)
                   }
diff --git a/packages/compiler-ssr/__tests__/ssrElement.spec.ts b/packages/compiler-ssr/__tests__/ssrElement.spec.ts
index f1d509acfb0..d344405f3ed 100644
--- a/packages/compiler-ssr/__tests__/ssrElement.spec.ts
+++ b/packages/compiler-ssr/__tests__/ssrElement.spec.ts
@@ -396,4 +396,50 @@ describe('ssr: element', () => {
       `)
     })
   })
+
+  describe('dynamic anchor', () => {
+    test('two consecutive components', () => {
+      expect(
+        getCompiledString(`
+        <div>
+          <div/>
+          <Comp1/>
+          <Comp2/>
+          <div/>
+        </div>
+        `),
+      ).toMatchInlineSnapshot(`
+        "\`<div><div></div>\`)
+          _push(_ssrRenderComponent(_component_Comp1, null, null, _parent))
+          _push(\`<!--[[-->\`)
+          _push(_ssrRenderComponent(_component_Comp2, null, null, _parent))
+          _push(\`<!--]]--><div></div></div>\`"
+      `)
+    })
+
+    test('multiple consecutive components', () => {
+      expect(
+        getCompiledString(`
+        <div>
+          <div/>
+          <Comp1/>
+          <Comp2/>
+          <Comp3/>
+          <Comp4/>
+          <div/>
+        </div>
+        `),
+      ).toMatchInlineSnapshot(`
+        "\`<div><div></div>\`)
+          _push(_ssrRenderComponent(_component_Comp1, null, null, _parent))
+          _push(\`<!--[[-->\`)
+          _push(_ssrRenderComponent(_component_Comp2, null, null, _parent))
+          _push(\`<!--]]--><!--[[-->\`)
+          _push(_ssrRenderComponent(_component_Comp3, null, null, _parent))
+          _push(\`<!--]]-->\`)
+          _push(_ssrRenderComponent(_component_Comp4, null, null, _parent))
+          _push(\`<div></div></div>\`"
+      `)
+    })
+  })
 })
diff --git a/packages/compiler-ssr/__tests__/ssrFallthroughAttrs.spec.ts b/packages/compiler-ssr/__tests__/ssrFallthroughAttrs.spec.ts
index 7b3d1962c3e..712c09d0946 100644
--- a/packages/compiler-ssr/__tests__/ssrFallthroughAttrs.spec.ts
+++ b/packages/compiler-ssr/__tests__/ssrFallthroughAttrs.spec.ts
@@ -29,6 +29,7 @@ describe('ssr: attrs fallthrough', () => {
         _push(\`<!--[-->\`)
         if (true) {
           _push(\`<div></div>\`)
+          _push(\`<!--if-->\`)
         } else {
           _push(\`<!---->\`)
         }
diff --git a/packages/compiler-ssr/__tests__/ssrInjectCssVars.spec.ts b/packages/compiler-ssr/__tests__/ssrInjectCssVars.spec.ts
index 9e70dac0bdc..0666e8949cc 100644
--- a/packages/compiler-ssr/__tests__/ssrInjectCssVars.spec.ts
+++ b/packages/compiler-ssr/__tests__/ssrInjectCssVars.spec.ts
@@ -70,6 +70,7 @@ describe('ssr: inject <style vars>', () => {
         const _cssVars = { style: { color: _ctx.color }}
         if (_ctx.ok) {
           _push(\`<div\${_ssrRenderAttrs(_mergeProps(_attrs, _cssVars))}></div>\`)
+          _push(\`<!--if-->\`)
         } else {
           _push(\`<!--[--><div\${
             _ssrRenderAttrs(_cssVars)
diff --git a/packages/compiler-ssr/__tests__/ssrSlotOutlet.spec.ts b/packages/compiler-ssr/__tests__/ssrSlotOutlet.spec.ts
index 86863cfb85f..4d73dfe0827 100644
--- a/packages/compiler-ssr/__tests__/ssrSlotOutlet.spec.ts
+++ b/packages/compiler-ssr/__tests__/ssrSlotOutlet.spec.ts
@@ -153,6 +153,7 @@ describe('ssr: <slot>', () => {
       return function ssrRender(_ctx, _push, _parent, _attrs) {
         if (true) {
           _ssrRenderSlotInner(_ctx.$slots, "default", {}, null, _push, _parent, null, true)
+          _push(\`<!--if-->\`)
         } else {
           _push(\`<!---->\`)
         }
diff --git a/packages/compiler-ssr/__tests__/ssrTransitionGroup.spec.ts b/packages/compiler-ssr/__tests__/ssrTransitionGroup.spec.ts
index 82122e621c7..73d4331a7d7 100644
--- a/packages/compiler-ssr/__tests__/ssrTransitionGroup.spec.ts
+++ b/packages/compiler-ssr/__tests__/ssrTransitionGroup.spec.ts
@@ -15,7 +15,7 @@ describe('transition-group', () => {
         _ssrRenderList(_ctx.list, (i) => {
           _push(\`<div></div>\`)
         })
-        _push(\`<!--]-->\`)
+        _push(\`<!--for--><!--]-->\`)
       }"
     `)
   })
@@ -33,7 +33,7 @@ describe('transition-group', () => {
         _ssrRenderList(_ctx.list, (i) => {
           _push(\`<div></div>\`)
         })
-        _push(\`</ul>\`)
+        _push(\`<!--for--></ul>\`)
       }"
     `)
   })
@@ -52,8 +52,10 @@ describe('transition-group', () => {
         _ssrRenderList(_ctx.list, (i) => {
           _push(\`<div></div>\`)
         })
+        _push(\`<!--for-->\`)
         if (false) {
           _push(\`<div></div>\`)
+          _push(\`<!--if-->\`)
         }
         _push(\`</ul>\`)
       }"
@@ -74,7 +76,7 @@ describe('transition-group', () => {
         _ssrRenderList(_ctx.list, (i) => {
           _push(\`<div></div>\`)
         })
-        _push(\`</ul>\`)
+        _push(\`<!--for--></ul>\`)
       }"
     `)
   })
@@ -96,7 +98,7 @@ describe('transition-group', () => {
         _ssrRenderList(_ctx.list, (i) => {
           _push(\`<div></div>\`)
         })
-        _push(\`</\${_ctx.someTag}>\`)
+        _push(\`<!--for--></\${_ctx.someTag}>\`)
       }"
     `)
   })
@@ -118,11 +120,14 @@ describe('transition-group', () => {
         _ssrRenderList(10, (i) => {
           _push(\`<div></div>\`)
         })
+        _push(\`<!--for-->\`)
         _ssrRenderList(10, (i) => {
           _push(\`<div></div>\`)
         })
+        _push(\`<!--for-->\`)
         if (_ctx.ok) {
           _push(\`<div>ok</div>\`)
+          _push(\`<!--if-->\`)
         }
         _push(\`<!--]-->\`)
       }"
diff --git a/packages/compiler-ssr/__tests__/ssrVFor.spec.ts b/packages/compiler-ssr/__tests__/ssrVFor.spec.ts
index 0d957265120..dad426de04c 100644
--- a/packages/compiler-ssr/__tests__/ssrVFor.spec.ts
+++ b/packages/compiler-ssr/__tests__/ssrVFor.spec.ts
@@ -10,7 +10,7 @@ describe('ssr: v-for', () => {
         _ssrRenderList(_ctx.list, (i) => {
           _push(\`<div></div>\`)
         })
-        _push(\`<!--]-->\`)
+        _push(\`<!--]--><!--for-->\`)
       }"
     `)
   })
@@ -25,7 +25,7 @@ describe('ssr: v-for', () => {
           _ssrRenderList(_ctx.list, (i) => {
             _push(\`<div>foo<span>bar</span></div>\`)
           })
-          _push(\`<!--]-->\`)
+          _push(\`<!--]--><!--for-->\`)
         }"
       `)
   })
@@ -51,9 +51,9 @@ describe('ssr: v-for', () => {
               _ssrInterpolate(j)
             }</div>\`)
           })
-          _push(\`<!--]--></div>\`)
+          _push(\`<!--]--><!--for--></div>\`)
         })
-        _push(\`<!--]-->\`)
+        _push(\`<!--]--><!--for-->\`)
       }"
     `)
   })
@@ -68,7 +68,7 @@ describe('ssr: v-for', () => {
           _ssrRenderList(_ctx.list, (i) => {
             _push(\`<!--[-->\${_ssrInterpolate(i)}<!--]-->\`)
           })
-          _push(\`<!--]-->\`)
+          _push(\`<!--]--><!--for-->\`)
         }"
       `)
   })
@@ -85,7 +85,7 @@ describe('ssr: v-for', () => {
         _ssrRenderList(_ctx.list, (i) => {
           _push(\`<span>\${_ssrInterpolate(i)}</span>\`)
         })
-        _push(\`<!--]-->\`)
+        _push(\`<!--]--><!--for-->\`)
       }"
     `)
   })
@@ -107,7 +107,7 @@ describe('ssr: v-for', () => {
             _ssrInterpolate(i + 1)
           }</span><!--]-->\`)
         })
-        _push(\`<!--]-->\`)
+        _push(\`<!--]--><!--for-->\`)
       }"
     `)
   })
@@ -127,7 +127,7 @@ describe('ssr: v-for', () => {
         _ssrRenderList(_ctx.list, ({ foo }, index) => {
           _push(\`<div>\${_ssrInterpolate(foo + _ctx.bar + index)}</div>\`)
         })
-        _push(\`<!--]-->\`)
+        _push(\`<!--]--><!--for-->\`)
       }"
     `)
   })
diff --git a/packages/compiler-ssr/__tests__/ssrVIf.spec.ts b/packages/compiler-ssr/__tests__/ssrVIf.spec.ts
index b544adadcf3..840d485088b 100644
--- a/packages/compiler-ssr/__tests__/ssrVIf.spec.ts
+++ b/packages/compiler-ssr/__tests__/ssrVIf.spec.ts
@@ -8,6 +8,7 @@ describe('ssr: v-if', () => {
       return function ssrRender(_ctx, _push, _parent, _attrs) {
         if (_ctx.foo) {
           _push(\`<div\${_ssrRenderAttrs(_attrs)}></div>\`)
+          _push(\`<!--if-->\`)
         } else {
           _push(\`<!---->\`)
         }
@@ -23,6 +24,7 @@ describe('ssr: v-if', () => {
         return function ssrRender(_ctx, _push, _parent, _attrs) {
           if (_ctx.foo) {
             _push(\`<div\${_ssrRenderAttrs(_attrs)}>hello<span>ok</span></div>\`)
+            _push(\`<!--if-->\`)
           } else {
             _push(\`<!---->\`)
           }
@@ -38,6 +40,7 @@ describe('ssr: v-if', () => {
         return function ssrRender(_ctx, _push, _parent, _attrs) {
           if (_ctx.foo) {
             _push(\`<div\${_ssrRenderAttrs(_attrs)}></div>\`)
+            _push(\`<!--if-->\`)
           } else {
             _push(\`<span\${_ssrRenderAttrs(_attrs)}></span>\`)
           }
@@ -53,8 +56,10 @@ describe('ssr: v-if', () => {
         return function ssrRender(_ctx, _push, _parent, _attrs) {
           if (_ctx.foo) {
             _push(\`<div\${_ssrRenderAttrs(_attrs)}></div>\`)
+            _push(\`<!--if-->\`)
           } else if (_ctx.bar) {
             _push(\`<span\${_ssrRenderAttrs(_attrs)}></span>\`)
+            _push(\`<!--if-->\`)
           } else {
             _push(\`<!---->\`)
           }
@@ -70,8 +75,10 @@ describe('ssr: v-if', () => {
         return function ssrRender(_ctx, _push, _parent, _attrs) {
           if (_ctx.foo) {
             _push(\`<div\${_ssrRenderAttrs(_attrs)}></div>\`)
+            _push(\`<!--if-->\`)
           } else if (_ctx.bar) {
             _push(\`<span\${_ssrRenderAttrs(_attrs)}></span>\`)
+            _push(\`<!--if-->\`)
           } else {
             _push(\`<p\${_ssrRenderAttrs(_attrs)}></p>\`)
           }
@@ -82,15 +89,16 @@ describe('ssr: v-if', () => {
   test('<template v-if> (text)', () => {
     expect(compile(`<template v-if="foo">hello</template>`).code)
       .toMatchInlineSnapshot(`
-      "
-      return function ssrRender(_ctx, _push, _parent, _attrs) {
-        if (_ctx.foo) {
-          _push(\`<!--[-->hello<!--]-->\`)
-        } else {
-          _push(\`<!---->\`)
-        }
-      }"
-    `)
+        "
+        return function ssrRender(_ctx, _push, _parent, _attrs) {
+          if (_ctx.foo) {
+            _push(\`<!--[-->hello<!--]-->\`)
+            _push(\`<!--if-->\`)
+          } else {
+            _push(\`<!---->\`)
+          }
+        }"
+      `)
   })
 
   test('<template v-if> (single element)', () => {
@@ -102,6 +110,7 @@ describe('ssr: v-if', () => {
         return function ssrRender(_ctx, _push, _parent, _attrs) {
           if (_ctx.foo) {
             _push(\`<div\${_ssrRenderAttrs(_attrs)}>hi</div>\`)
+            _push(\`<!--if-->\`)
           } else {
             _push(\`<!---->\`)
           }
@@ -118,6 +127,7 @@ describe('ssr: v-if', () => {
       return function ssrRender(_ctx, _push, _parent, _attrs) {
         if (_ctx.foo) {
           _push(\`<!--[--><div>hi</div><div>ho</div><!--]-->\`)
+          _push(\`<!--if-->\`)
         } else {
           _push(\`<!---->\`)
         }
@@ -137,7 +147,8 @@ describe('ssr: v-if', () => {
           _ssrRenderList(_ctx.list, (i) => {
             _push(\`<div></div>\`)
           })
-          _push(\`<!--]-->\`)
+          _push(\`<!--]--><!--for-->\`)
+          _push(\`<!--if-->\`)
         } else {
           _push(\`<!---->\`)
         }
@@ -156,6 +167,7 @@ describe('ssr: v-if', () => {
       return function ssrRender(_ctx, _push, _parent, _attrs) {
         if (_ctx.foo) {
           _push(\`<!--[--><div>hi</div><div>ho</div><!--]-->\`)
+          _push(\`<!--if-->\`)
         } else {
           _push(\`<div\${_ssrRenderAttrs(_attrs)}></div>\`)
         }
diff --git a/packages/compiler-ssr/__tests__/ssrVModel.spec.ts b/packages/compiler-ssr/__tests__/ssrVModel.spec.ts
index 0bf7673d00d..c88f6ba3182 100644
--- a/packages/compiler-ssr/__tests__/ssrVModel.spec.ts
+++ b/packages/compiler-ssr/__tests__/ssrVModel.spec.ts
@@ -70,7 +70,7 @@ describe('ssr: v-model', () => {
               : _ssrLooseEqual(_ctx.model, i))) ? " selected" : ""
           }></option>\`)
         })
-        _push(\`<!--]--></select></div>\`)
+        _push(\`<!--]--><!--for--></select></div>\`)
       }"
     `)
 
@@ -91,6 +91,7 @@ describe('ssr: v-model', () => {
               ? _ssrLooseContain(_ctx.model, _ctx.i)
               : _ssrLooseEqual(_ctx.model, _ctx.i))) ? " selected" : ""
           }></option>\`)
+          _push(\`<!--if-->\`)
         } else {
           _push(\`<!---->\`)
         }
diff --git a/packages/compiler-ssr/src/ssrCodegenTransform.ts b/packages/compiler-ssr/src/ssrCodegenTransform.ts
index 536cbb5c1e9..852e02820cc 100644
--- a/packages/compiler-ssr/src/ssrCodegenTransform.ts
+++ b/packages/compiler-ssr/src/ssrCodegenTransform.ts
@@ -7,6 +7,7 @@ import {
   type IfStatement,
   type JSChildNode,
   NodeTypes,
+  type PlainElementNode,
   type RootNode,
   type TemplateChildNode,
   type TemplateLiteral,
@@ -20,7 +21,12 @@ import {
   isText,
   processExpression,
 } from '@vue/compiler-dom'
-import { escapeHtml, isString } from '@vue/shared'
+import {
+  DYNAMIC_END_ANCHOR_LABEL,
+  DYNAMIC_START_ANCHOR_LABEL,
+  escapeHtml,
+  isString,
+} from '@vue/shared'
 import { SSR_INTERPOLATE, ssrHelpers } from './runtimeHelpers'
 import { ssrProcessIf } from './transforms/ssrVIf'
 import { ssrProcessFor } from './transforms/ssrVFor'
@@ -157,13 +163,33 @@ export function processChildren(
   asFragment = false,
   disableNestedFragments = false,
   disableComment = false,
+  asDynamic = false,
 ): void {
+  if (asDynamic) {
+    context.pushStringPart(`<!--${DYNAMIC_START_ANCHOR_LABEL}-->`)
+  }
   if (asFragment) {
     context.pushStringPart(`<!--[-->`)
   }
-  const { children } = parent
+
+  const { children, type, tagType } = parent as PlainElementNode
+  const inElement =
+    type === NodeTypes.ELEMENT && tagType === ElementTypes.ELEMENT
+  if (inElement) processChildrenDynamicInfo(children)
+
   for (let i = 0; i < children.length; i++) {
     const child = children[i]
+    if (inElement && shouldProcessChildAsDynamic(parent, child)) {
+      processChildren(
+        { children: [child] },
+        context,
+        asFragment,
+        disableNestedFragments,
+        disableComment,
+        true,
+      )
+      continue
+    }
     switch (child.type) {
       case NodeTypes.ELEMENT:
         switch (child.tagType) {
@@ -237,6 +263,9 @@ export function processChildren(
   if (asFragment) {
     context.pushStringPart(`<!--]-->`)
   }
+  if (asDynamic) {
+    context.pushStringPart(`<!--${DYNAMIC_END_ANCHOR_LABEL}-->`)
+  }
 }
 
 export function processChildrenAsStatement(
@@ -249,3 +278,147 @@ export function processChildrenAsStatement(
   processChildren(parent, childContext, asFragment)
   return createBlockStatement(childContext.body)
 }
+
+const isStaticChildNode = (c: TemplateChildNode): boolean =>
+  (c.type === NodeTypes.ELEMENT && c.tagType !== ElementTypes.COMPONENT) ||
+  c.type === NodeTypes.TEXT ||
+  c.type === NodeTypes.COMMENT
+
+interface DynamicInfo {
+  hasStaticPrevious: boolean
+  hasStaticNext: boolean
+  prevDynamicCount: number
+  nextDynamicCount: number
+}
+
+function processChildrenDynamicInfo(
+  children: (TemplateChildNode & { _ssrDynamicInfo?: DynamicInfo })[],
+): void {
+  const filteredChildren = children.filter(
+    child => !(child.type === NodeTypes.TEXT && !child.content.trim()),
+  )
+
+  for (let i = 0; i < filteredChildren.length; i++) {
+    const child = filteredChildren[i]
+    if (
+      isStaticChildNode(child) ||
+      // fragment has it's own anchor, which can be used to distinguish the boundary
+      isFragmentChild(child)
+    ) {
+      continue
+    }
+    child._ssrDynamicInfo = {
+      hasStaticPrevious: false,
+      hasStaticNext: false,
+      prevDynamicCount: 0,
+      nextDynamicCount: 0,
+    }
+
+    const info = child._ssrDynamicInfo
+
+    // Calculate the previous static and dynamic node counts
+    let foundStaticPrev = false
+    let dynamicCountPrev = 0
+    for (let j = i - 1; j >= 0; j--) {
+      const prevChild = filteredChildren[j]
+      if (isStaticChildNode(prevChild)) {
+        foundStaticPrev = true
+        break
+      }
+      // if the previous child has dynamic info, use it
+      else if (prevChild._ssrDynamicInfo) {
+        foundStaticPrev = prevChild._ssrDynamicInfo.hasStaticPrevious
+        dynamicCountPrev = prevChild._ssrDynamicInfo.prevDynamicCount + 1
+        break
+      }
+      dynamicCountPrev++
+    }
+    info.hasStaticPrevious = foundStaticPrev
+    info.prevDynamicCount = dynamicCountPrev
+
+    // Calculate the number of static and dynamic nodes afterwards
+    let foundStaticNext = false
+    let dynamicCountNext = 0
+    for (let j = i + 1; j < filteredChildren.length; j++) {
+      const nextChild = filteredChildren[j]
+      if (isStaticChildNode(nextChild)) {
+        foundStaticNext = true
+        break
+      }
+      // if the next child has dynamic info, use it
+      else if (nextChild._ssrDynamicInfo) {
+        foundStaticNext = nextChild._ssrDynamicInfo.hasStaticNext
+        dynamicCountNext = nextChild._ssrDynamicInfo.nextDynamicCount + 1
+        break
+      }
+      dynamicCountNext++
+    }
+    info.hasStaticNext = foundStaticNext
+    info.nextDynamicCount = dynamicCountNext
+  }
+}
+
+/**
+ * Check if a node should be processed as dynamic child.
+ * This is primarily used in Vapor mode hydration to wrap dynamic parts
+ * with markers (`<!--[[-->` and `<!--]]-->`).
+ * The purpose is to distinguish the boundaries of nodes during vapor hydration
+ *
+ * 1. two consecutive dynamic nodes should only wrap the second one
+ * <element>
+ *   <element/>  // Static node
+ *   <Comp/>     // Dynamic node -> should NOT be wrapped
+ *   <Comp/>     // Dynamic node -> should be wrapped
+ *   <element/>  // Static node
+ * </element>
+ *
+ * 2. three or more consecutive dynamic nodes should only wrap the
+ *    middle nodes, leaving the first and last static.
+ * <element>
+ *  <element/>  // Static node
+ *  <Comp/>     // Dynamic node -> should NOT be wrapped
+ *  <Comp/>     // Dynamic node -> should be wrapped
+ *  <Comp/>     // Dynamic node -> should be wrapped
+ *  <Comp/>     // Dynamic node -> should NOT be wrapped
+ *  <element/>  // Static node
+ * </element>
+ */
+function shouldProcessChildAsDynamic(
+  parent: { tag?: string; children: TemplateChildNode[] },
+  node: TemplateChildNode & { _ssrDynamicInfo?: DynamicInfo },
+): boolean {
+  // must be inside a parent element
+  if (!parent.tag) return false
+
+  // must has dynamic info
+  const { _ssrDynamicInfo: info } = node
+  if (!info) return false
+
+  const {
+    hasStaticPrevious,
+    hasStaticNext,
+    prevDynamicCount,
+    nextDynamicCount,
+  } = info
+
+  // must have static nodes on both sides
+  if (!hasStaticPrevious || !hasStaticNext) return false
+
+  const dynamicNodeCount = 1 + prevDynamicCount + nextDynamicCount
+
+  // For two consecutive dynamic nodes, mark the second one as dynamic
+  if (dynamicNodeCount === 2) {
+    return prevDynamicCount > 0
+  }
+  // For three or more dynamic nodes, mark the middle nodes as dynamic
+  else if (dynamicNodeCount >= 3) {
+    return prevDynamicCount > 0 && nextDynamicCount > 0
+  }
+
+  return false
+}
+
+function isFragmentChild(child: TemplateChildNode): boolean {
+  const { type } = child
+  return type === NodeTypes.IF || type === NodeTypes.FOR
+}
diff --git a/packages/compiler-ssr/src/transforms/ssrTransformComponent.ts b/packages/compiler-ssr/src/transforms/ssrTransformComponent.ts
index cad1ee81028..419c2e4e49c 100644
--- a/packages/compiler-ssr/src/transforms/ssrTransformComponent.ts
+++ b/packages/compiler-ssr/src/transforms/ssrTransformComponent.ts
@@ -55,7 +55,14 @@ import {
   ssrProcessTransitionGroup,
   ssrTransformTransitionGroup,
 } from './ssrTransformTransitionGroup'
-import { extend, isArray, isObject, isPlainObject, isSymbol } from '@vue/shared'
+import {
+  DYNAMIC_COMPONENT_ANCHOR_LABEL,
+  extend,
+  isArray,
+  isObject,
+  isPlainObject,
+  isSymbol,
+} from '@vue/shared'
 import { buildSSRProps } from './ssrTransformElement'
 import {
   ssrProcessTransition,
@@ -264,6 +271,8 @@ export function ssrProcessComponent(
       // dynamic component (`resolveDynamicComponent` call)
       // the codegen node is a `renderVNode` call
       context.pushStatement(node.ssrCodegenNode)
+      // anchor for dynamic component for vapor hydration
+      context.pushStringPart(`<!--${DYNAMIC_COMPONENT_ANCHOR_LABEL}-->`)
     }
   }
 }
diff --git a/packages/compiler-ssr/src/transforms/ssrVFor.ts b/packages/compiler-ssr/src/transforms/ssrVFor.ts
index 6537eee8287..251b1fef5c9 100644
--- a/packages/compiler-ssr/src/transforms/ssrVFor.ts
+++ b/packages/compiler-ssr/src/transforms/ssrVFor.ts
@@ -13,6 +13,7 @@ import {
   processChildrenAsStatement,
 } from '../ssrCodegenTransform'
 import { SSR_RENDER_LIST } from '../runtimeHelpers'
+import { FOR_ANCHOR_LABEL } from '@vue/shared'
 
 // Plugin for the first transform pass, which simply constructs the AST node
 export const ssrTransformFor: NodeTransform =
@@ -49,4 +50,6 @@ export function ssrProcessFor(
   if (!disableNestedFragments) {
     context.pushStringPart(`<!--]-->`)
   }
+  // v-for anchor for vapor hydration
+  context.pushStringPart(`<!--${FOR_ANCHOR_LABEL}-->`)
 }
diff --git a/packages/compiler-ssr/src/transforms/ssrVIf.ts b/packages/compiler-ssr/src/transforms/ssrVIf.ts
index 0e3880247a1..811252c0fab 100644
--- a/packages/compiler-ssr/src/transforms/ssrVIf.ts
+++ b/packages/compiler-ssr/src/transforms/ssrVIf.ts
@@ -14,6 +14,7 @@ import {
   type SSRTransformContext,
   processChildrenAsStatement,
 } from '../ssrCodegenTransform'
+import { IF_ANCHOR_LABEL } from '@vue/shared'
 
 // Plugin for the first transform pass, which simply constructs the AST node
 export const ssrTransformIf: NodeTransform = createStructuralDirectiveTransform(
@@ -74,5 +75,16 @@ function processIfBranch(
     (children.length !== 1 || children[0].type !== NodeTypes.ELEMENT) &&
     // optimize away nested fragments when the only child is a ForNode
     !(children.length === 1 && children[0].type === NodeTypes.FOR)
-  return processChildrenAsStatement(branch, context, needFragmentWrapper)
+  const statement = processChildrenAsStatement(
+    branch,
+    context,
+    needFragmentWrapper,
+  )
+  if (branch.condition) {
+    // v-if/v-else-if anchor for vapor hydration
+    statement.body.push(
+      createCallExpression(`_push`, [`\`<!--${IF_ANCHOR_LABEL}-->\``]),
+    )
+  }
+  return statement
 }
diff --git a/packages/compiler-vapor/__tests__/__snapshots__/compile.spec.ts.snap b/packages/compiler-vapor/__tests__/__snapshots__/compile.spec.ts.snap
index e56676d8706..d4a8b6827bb 100644
--- a/packages/compiler-vapor/__tests__/__snapshots__/compile.spec.ts.snap
+++ b/packages/compiler-vapor/__tests__/__snapshots__/compile.spec.ts.snap
@@ -157,9 +157,9 @@ export function render(_ctx, $props, $emit, $attrs, $slots) {
   const _component_Comp = _resolveComponent("Comp")
   const n0 = t0()
   const n3 = t1()
+  const n2 = _child(n3)
   _setInsertionState(n3, 0)
   const n1 = _createComponentWithFallback(_component_Comp)
-  const n2 = _child(n3)
   _renderEffect(() => {
     _setText(n2, _toDisplayString(_ctx.bar))
     _setProp(n3, "id", _ctx.foo)
@@ -230,6 +230,30 @@ export function render(_ctx) {
 }"
 `;
 
+exports[`compile > setInsertionState > next, child and nthChild should be above the setInsertionState 1`] = `
+"import { resolveComponent as _resolveComponent, child as _child, next as _next, setInsertionState as _setInsertionState, createComponentWithFallback as _createComponentWithFallback, nthChild as _nthChild, createIf as _createIf, setProp as _setProp, renderEffect as _renderEffect, template as _template } from 'vue';
+const t0 = _template("<div></div>")
+const t1 = _template("<div><div></div><!><div></div><!><div><button></button></div></div>", true)
+
+export function render(_ctx) {
+  const _component_Comp = _resolveComponent("Comp")
+  const n6 = t1()
+  const n5 = _next(_child(n6))
+  const n7 = _nthChild(n6, 3)
+  const p0 = _next(n7)
+  const n4 = _child(p0)
+  _setInsertionState(n6, n5)
+  const n0 = _createComponentWithFallback(_component_Comp)
+  _setInsertionState(n6, n7)
+  const n1 = _createIf(() => (true), () => {
+    const n3 = t0()
+    return n3
+  })
+  _renderEffect(() => _setProp(n4, "disabled", _ctx.foo))
+  return n6
+}"
+`;
+
 exports[`compile > static + dynamic root 1`] = `
 "import { toDisplayString as _toDisplayString, setText as _setText, template as _template } from 'vue';
 const t0 = _template(" ")
diff --git a/packages/compiler-vapor/__tests__/compile.spec.ts b/packages/compiler-vapor/__tests__/compile.spec.ts
index 33f399caa77..3a2ce41f0cd 100644
--- a/packages/compiler-vapor/__tests__/compile.spec.ts
+++ b/packages/compiler-vapor/__tests__/compile.spec.ts
@@ -220,4 +220,21 @@ describe('compile', () => {
       expect(code).matchSnapshot()
     })
   })
+
+  describe('setInsertionState', () => {
+    test('next, child and nthChild should be above the setInsertionState', () => {
+      const code = compile(`
+      <div>
+        <div />
+        <Comp />
+        <div />
+        <div v-if="true" />
+        <div>
+          <button :disabled="foo" />
+        </div>
+      </div>
+      `)
+      expect(code).toMatchSnapshot()
+    })
+  })
 })
diff --git a/packages/compiler-vapor/__tests__/transforms/__snapshots__/transformTemplateRef.spec.ts.snap b/packages/compiler-vapor/__tests__/transforms/__snapshots__/transformTemplateRef.spec.ts.snap
index f2eade4bcdf..d823c4ed0af 100644
--- a/packages/compiler-vapor/__tests__/transforms/__snapshots__/transformTemplateRef.spec.ts.snap
+++ b/packages/compiler-vapor/__tests__/transforms/__snapshots__/transformTemplateRef.spec.ts.snap
@@ -43,6 +43,24 @@ export function render(_ctx) {
 }"
 `;
 
+exports[`compiler: template ref transform > static ref (PROD) 1`] = `
+"
+  const _setTemplateRef = _createTemplateRefSetter()
+  const n0 = t0()
+  _setTemplateRef(n0, foo)
+  return n0
+"
+`;
+
+exports[`compiler: template ref transform > static ref (inline mode) 1`] = `
+"
+  const _setTemplateRef = _createTemplateRefSetter()
+  const n0 = t0()
+  _setTemplateRef(n0, foo)
+  return n0
+"
+`;
+
 exports[`compiler: template ref transform > static ref 1`] = `
 "import { createTemplateRefSetter as _createTemplateRefSetter, template as _template } from 'vue';
 const t0 = _template("<div></div>", true)
diff --git a/packages/compiler-vapor/__tests__/transforms/transformChildren.spec.ts b/packages/compiler-vapor/__tests__/transforms/transformChildren.spec.ts
index e656312356c..2d8ae8c960d 100644
--- a/packages/compiler-vapor/__tests__/transforms/transformChildren.spec.ts
+++ b/packages/compiler-vapor/__tests__/transforms/transformChildren.spec.ts
@@ -69,8 +69,8 @@ describe('compiler: children transform', () => {
       </div>`,
     )
     // ensure the insertion anchor is generated before the insertion statement
-    expect(code).toMatch(`const n3 = _next(_child(n4))
-  _setInsertionState(n4, n3)`)
+    expect(code).toMatch(`const n3 = _next(_child(n4))`)
+    expect(code).toMatch(`_setInsertionState(n4, n3)`)
     expect(code).toMatchSnapshot()
   })
 })
diff --git a/packages/compiler-vapor/__tests__/transforms/transformTemplateRef.spec.ts b/packages/compiler-vapor/__tests__/transforms/transformTemplateRef.spec.ts
index 6be8f18779c..3dc9c80876b 100644
--- a/packages/compiler-vapor/__tests__/transforms/transformTemplateRef.spec.ts
+++ b/packages/compiler-vapor/__tests__/transforms/transformTemplateRef.spec.ts
@@ -1,3 +1,4 @@
+import { BindingTypes } from '@vue/compiler-dom'
 import {
   DynamicFlag,
   type ForIRNode,
@@ -48,6 +49,16 @@ describe('compiler: template ref transform', () => {
     expect(code).contains('_setTemplateRef(n0, "foo")')
   })
 
+  test('static ref (inline mode)', () => {
+    const { code } = compileWithTransformRef(`<div ref="foo" />`, {
+      inline: true,
+      bindingMetadata: { foo: BindingTypes.SETUP_REF },
+    })
+    expect(code).matchSnapshot()
+    // pass the actual ref
+    expect(code).contains('_setTemplateRef(n0, foo)')
+  })
+
   test('dynamic ref', () => {
     const { ir, code } = compileWithTransformRef(`<div :ref="foo" />`)
 
diff --git a/packages/compiler-vapor/src/generators/block.ts b/packages/compiler-vapor/src/generators/block.ts
index b161b8f45d1..48e4b5cb890 100644
--- a/packages/compiler-vapor/src/generators/block.ts
+++ b/packages/compiler-vapor/src/generators/block.ts
@@ -52,7 +52,7 @@ export function genBlockContent(
     push(...genSelf(child, context))
   }
   for (const child of dynamic.children) {
-    push(...genChildren(child, context, `n${child.id!}`))
+    push(...genChildren(child, context, push, `n${child.id!}`))
   }
 
   push(...genOperations(operation, context))
diff --git a/packages/compiler-vapor/src/generators/operation.ts b/packages/compiler-vapor/src/generators/operation.ts
index 4247bc6feca..a3bf5cc2193 100644
--- a/packages/compiler-vapor/src/generators/operation.ts
+++ b/packages/compiler-vapor/src/generators/operation.ts
@@ -44,7 +44,7 @@ export function genOperationWithInsertionState(
 ): CodeFragment[] {
   const [frag, push] = buildCodeFragment()
   if (isBlockOperation(oper) && oper.parent) {
-    push(...genInsertionstate(oper, context))
+    push(...genInsertionState(oper, context))
   }
   push(...genOperation(oper, context))
   return frag
@@ -152,7 +152,7 @@ export function genEffect(
   return frag
 }
 
-function genInsertionstate(
+function genInsertionState(
   operation: InsertionStateTypes,
   context: CodegenContext,
 ): CodeFragment[] {
diff --git a/packages/compiler-vapor/src/generators/template.ts b/packages/compiler-vapor/src/generators/template.ts
index 356c1ccbe15..5a066b09e9a 100644
--- a/packages/compiler-vapor/src/generators/template.ts
+++ b/packages/compiler-vapor/src/generators/template.ts
@@ -41,6 +41,7 @@ export function genSelf(
 export function genChildren(
   dynamic: IRDynamicInfo,
   context: CodegenContext,
+  pushBlock: (...items: CodeFragment[]) => number,
   from: string = `n${dynamic.id}`,
 ): CodeFragment[] {
   const { helper } = context
@@ -72,17 +73,17 @@ export function genChildren(
     // p for "placeholder" variables that are meant for possible reuse by
     // other access paths
     const variable = id === undefined ? `p${context.block.tempId++}` : `n${id}`
-    push(NEWLINE, `const ${variable} = `)
+    pushBlock(NEWLINE, `const ${variable} = `)
 
     if (prev) {
       if (elementIndex - prev[1] === 1) {
-        push(...genCall(helper('next'), prev[0]))
+        pushBlock(...genCall(helper('next'), prev[0]))
       } else {
-        push(...genCall(helper('nthChild'), from, String(elementIndex)))
+        pushBlock(...genCall(helper('nthChild'), from, String(elementIndex)))
       }
     } else {
       if (elementIndex === 0) {
-        push(...genCall(helper('child'), from))
+        pushBlock(...genCall(helper('child'), from))
       } else {
         // check if there's a node that we can reuse from
         let init = genCall(helper('child'), from)
@@ -91,7 +92,7 @@ export function genChildren(
         } else if (elementIndex > 1) {
           init = genCall(helper('nthChild'), from, String(elementIndex))
         }
-        push(...init)
+        pushBlock(...init)
       }
     }
 
@@ -109,7 +110,7 @@ export function genChildren(
 
   if (childrenToGen.length) {
     for (const [child, from] of childrenToGen) {
-      push(...genChildren(child, context, from))
+      push(...genChildren(child, context, pushBlock, from))
     }
   }
 
diff --git a/packages/compiler-vapor/src/generators/templateRef.ts b/packages/compiler-vapor/src/generators/templateRef.ts
index a4d6d546ed3..af8facc57b1 100644
--- a/packages/compiler-vapor/src/generators/templateRef.ts
+++ b/packages/compiler-vapor/src/generators/templateRef.ts
@@ -2,6 +2,7 @@ import { genExpression } from './expression'
 import type { CodegenContext } from '../generate'
 import type { DeclareOldRefIRNode, SetTemplateRefIRNode } from '../ir'
 import { type CodeFragment, NEWLINE, genCall } from './utils'
+import { BindingTypes, type SimpleExpressionNode } from '@vue/compiler-dom'
 
 export const setTemplateRefIdent = `_setTemplateRef`
 
@@ -15,7 +16,7 @@ export function genSetTemplateRef(
     ...genCall(
       setTemplateRefIdent, // will be generated in root scope
       `n${oper.element}`,
-      genExpression(oper.value, context),
+      genRefValue(oper.value, context),
       oper.effect ? `r${oper.element}` : oper.refFor ? 'void 0' : undefined,
       oper.refFor && 'true',
     ),
@@ -25,3 +26,20 @@ export function genSetTemplateRef(
 export function genDeclareOldRef(oper: DeclareOldRefIRNode): CodeFragment[] {
   return [NEWLINE, `let r${oper.id}`]
 }
+
+function genRefValue(value: SimpleExpressionNode, context: CodegenContext) {
+  // in inline mode there is no setupState object, so we can't use string
+  // keys to set the ref. Instead, we need to transform it to pass the
+  // actual ref instead.
+  if (!__BROWSER__ && value && context.options.inline) {
+    const binding = context.options.bindingMetadata[value.content]
+    if (
+      binding === BindingTypes.SETUP_LET ||
+      binding === BindingTypes.SETUP_REF ||
+      binding === BindingTypes.SETUP_MAYBE_REF
+    ) {
+      return [value.content]
+    }
+  }
+  return genExpression(value, context)
+}
diff --git a/packages/compiler-vapor/src/transforms/transformChildren.ts b/packages/compiler-vapor/src/transforms/transformChildren.ts
index 790cd9d6fb1..da47438c2a8 100644
--- a/packages/compiler-vapor/src/transforms/transformChildren.ts
+++ b/packages/compiler-vapor/src/transforms/transformChildren.ts
@@ -70,10 +70,23 @@ function processDynamicChildren(context: TransformContext<ElementNode>) {
     if (!(child.flags & DynamicFlag.NON_TEMPLATE)) {
       if (prevDynamics.length) {
         if (hasStaticTemplate) {
-          context.childrenTemplate[index - prevDynamics.length] = `<!>`
-          prevDynamics[0].flags -= DynamicFlag.NON_TEMPLATE
-          const anchor = (prevDynamics[0].anchor = context.increaseId())
-          registerInsertion(prevDynamics, context, anchor)
+          // each dynamic child gets its own placeholder node.
+          // this makes it easier to locate the corresponding node during hydration.
+          for (let i = 0; i < prevDynamics.length; i++) {
+            const idx = index - prevDynamics.length + i
+            context.childrenTemplate[idx] = `<!>`
+            const dynamicChild = prevDynamics[i]
+            dynamicChild.flags -= DynamicFlag.NON_TEMPLATE
+            const anchor = (dynamicChild.anchor = context.increaseId())
+            if (
+              dynamicChild.operation &&
+              isBlockOperation(dynamicChild.operation)
+            ) {
+              // block types
+              dynamicChild.operation.parent = context.reference()
+              dynamicChild.operation.anchor = anchor
+            }
+          }
         } else {
           registerInsertion(prevDynamics, context, -1 /* prepend */)
         }
diff --git a/packages/runtime-core/__tests__/hydration.spec.ts b/packages/runtime-core/__tests__/hydration.spec.ts
index 56011d06359..793c11cede3 100644
--- a/packages/runtime-core/__tests__/hydration.spec.ts
+++ b/packages/runtime-core/__tests__/hydration.spec.ts
@@ -598,14 +598,14 @@ describe('SSR hydration', () => {
     const ctx: SSRContext = {}
     container.innerHTML = await renderToString(h(App), ctx)
     expect(container.innerHTML).toBe(
-      '<div><!--teleport start--><!--teleport end--></div>',
+      '<div><!--teleport start--><!--teleport end--><!--if--></div>',
     )
     teleportContainer.innerHTML = ctx.teleports!['#target']
 
     // hydrate
     createSSRApp(App).mount(container)
     expect(container.innerHTML).toBe(
-      '<div><!--teleport start--><!--teleport end--></div>',
+      '<div><!--teleport start--><!--teleport end--><!--if--></div>',
     )
     expect(teleportContainer.innerHTML).toBe(
       '<!--teleport start anchor--><span>Teleported Comp1</span><!--teleport anchor-->',
@@ -614,7 +614,7 @@ describe('SSR hydration', () => {
 
     toggle.value = false
     await nextTick()
-    expect(container.innerHTML).toBe('<div><div>Comp2</div></div>')
+    expect(container.innerHTML).toBe('<div><div>Comp2</div><!--if--></div>')
     expect(teleportContainer.innerHTML).toBe('')
   })
 
@@ -657,21 +657,21 @@ describe('SSR hydration', () => {
     // server render
     container.innerHTML = await renderToString(h(App))
     expect(container.innerHTML).toBe(
-      '<div><!--teleport start--><!--teleport end--></div>',
+      '<div><!--teleport start--><!--teleport end--><!--if--></div>',
     )
     expect(teleportContainer.innerHTML).toBe('')
 
     // hydrate
     createSSRApp(App).mount(container)
     expect(container.innerHTML).toBe(
-      '<div><!--teleport start--><!--teleport end--></div>',
+      '<div><!--teleport start--><!--teleport end--><!--if--></div>',
     )
     expect(teleportContainer.innerHTML).toBe('<span>Teleported Comp1</span>')
     expect(`Hydration children mismatch`).toHaveBeenWarned()
 
     toggle.value = false
     await nextTick()
-    expect(container.innerHTML).toBe('<div><div>Comp2</div></div>')
+    expect(container.innerHTML).toBe('<div><div>Comp2</div><!--if--></div>')
     expect(teleportContainer.innerHTML).toBe('')
   })
 
@@ -1843,6 +1843,36 @@ describe('SSR hydration', () => {
     }
   })
 
+  describe('dynamic anchor', () => {
+    test('two consecutive components', () => {
+      const Comp = {
+        render() {
+          return createTextVNode('foo')
+        },
+      }
+      const { vnode, container } = mountWithHydration(
+        `<div><span></span>foo<!--[[-->foo<!--]]--><span></span></div>`,
+        () => h('div', null, [h('span'), h(Comp), h(Comp), h('span')]),
+      )
+      expect(vnode.el).toBe(container.firstChild)
+      expect(`Hydration children mismatch`).not.toHaveBeenWarned()
+    })
+
+    test('multiple consecutive components', () => {
+      const Comp = {
+        render() {
+          return createTextVNode('foo')
+        },
+      }
+      const { vnode, container } = mountWithHydration(
+        `<div><span></span>foo<!--[[-->foo<!--]]-->foo<span></span></div>`,
+        () => h('div', null, [h('span'), h(Comp), h(Comp), h(Comp), h('span')]),
+      )
+      expect(vnode.el).toBe(container.firstChild)
+      expect(`Hydration children mismatch`).not.toHaveBeenWarned()
+    })
+  })
+
   describe('mismatch handling', () => {
     test('text node', () => {
       const { container } = mountWithHydration(`foo`, () => 'bar')
diff --git a/packages/runtime-core/src/apiCreateApp.ts b/packages/runtime-core/src/apiCreateApp.ts
index d8ae73fb69d..e34545f8b53 100644
--- a/packages/runtime-core/src/apiCreateApp.ts
+++ b/packages/runtime-core/src/apiCreateApp.ts
@@ -193,6 +193,7 @@ 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(node: Node, fn: () => void): void
 
   vdomMount: (component: ConcreteComponent, props?: any, slots?: any) => any
   vdomUnmount: UnmountComponentFn
diff --git a/packages/runtime-core/src/hydration.ts b/packages/runtime-core/src/hydration.ts
index ef6f1918c31..8ed7b6af189 100644
--- a/packages/runtime-core/src/hydration.ts
+++ b/packages/runtime-core/src/hydration.ts
@@ -31,11 +31,16 @@ import {
   isRenderableAttrValue,
   isReservedProp,
   isString,
+  isVaporAnchors,
   normalizeClass,
   normalizeStyle,
   stringifyStyle,
 } from '@vue/shared'
-import { type RendererInternals, needTransition } from './renderer'
+import {
+  type RendererInternals,
+  getVaporInterface,
+  needTransition,
+} from './renderer'
 import { setRef } from './rendererTemplateRef'
 import {
   type SuspenseBoundary,
@@ -111,7 +116,7 @@ export function createHydrationFunctions(
     o: {
       patchProp,
       createText,
-      nextSibling,
+      nextSibling: next,
       parentNode,
       remove,
       insert,
@@ -119,6 +124,15 @@ export function createHydrationFunctions(
     },
   } = rendererInternals
 
+  function nextSibling(node: Node) {
+    let n = next(node)
+    // skip vapor mode specific anchors
+    if (n && isVaporAnchors(n)) {
+      n = next(n)
+    }
+    return n
+  }
+
   const hydrate: RootHydrateFunction = (vnode, container) => {
     if (!container.hasChildNodes()) {
       ;(__DEV__ || __FEATURE_PROD_HYDRATION_MISMATCH_DETAILS__) &&
@@ -145,6 +159,10 @@ export function createHydrationFunctions(
     slotScopeIds: string[] | null,
     optimized = false,
   ): Node | null => {
+    // skip vapor mode specific anchors
+    if (isVaporAnchors(node)) {
+      node = nextSibling(node)!
+    }
     optimized = optimized || !!vnode.dynamicChildren
     const isFragmentStart = isComment(node) && node.data === '['
     const onMismatch = () =>
@@ -278,10 +296,6 @@ export function createHydrationFunctions(
             )
           }
         } else if (shapeFlag & ShapeFlags.COMPONENT) {
-          if ((vnode.type as ConcreteComponent).__vapor) {
-            throw new Error('Vapor component hydration is not supported yet.')
-          }
-
           // when setting up the render effect, if the initial vnode already
           // has .el set, the component will perform hydration instead of mount
           // on its sub-tree.
@@ -302,15 +316,23 @@ export function createHydrationFunctions(
             nextNode = nextSibling(node)
           }
 
-          mountComponent(
-            vnode,
-            container,
-            null,
-            parentComponent,
-            parentSuspense,
-            getContainerType(container),
-            optimized,
-          )
+          // hydrate vapor component
+          if ((vnode.type as ConcreteComponent).__vapor) {
+            const vaporInterface = getVaporInterface(parentComponent, vnode)
+            vaporInterface.hydrate(node, () => {
+              vaporInterface.mount(vnode, container, null, parentComponent)
+            })
+          } else {
+            mountComponent(
+              vnode,
+              container,
+              null,
+              parentComponent,
+              parentSuspense,
+              getContainerType(container),
+              optimized,
+            )
+          }
 
           // #3787
           // if component is async, it may get moved / unmounted before its
@@ -451,7 +473,7 @@ export function createHydrationFunctions(
 
           // The SSRed DOM contains more nodes than it should. Remove them.
           const cur = next
-          next = next.nextSibling
+          next = nextSibling(next)
           remove(cur)
         }
       } else if (shapeFlag & ShapeFlags.TEXT_CHILDREN) {
@@ -553,7 +575,7 @@ export function createHydrationFunctions(
       }
     }
 
-    return el.nextSibling
+    return nextSibling(el)
   }
 
   const hydrateChildren = (
diff --git a/packages/runtime-core/src/renderer.ts b/packages/runtime-core/src/renderer.ts
index 5a18d62a8e1..bb895a480ad 100644
--- a/packages/runtime-core/src/renderer.ts
+++ b/packages/runtime-core/src/renderer.ts
@@ -107,6 +107,7 @@ export interface Renderer<HostElement = RendererElement> {
 
 export interface HydrationRenderer extends Renderer<Element | ShadowRoot> {
   hydrate: RootHydrateFunction
+  hydrateNode: ReturnType<typeof createHydrationFunctions>[1]
 }
 
 export type ElementNamespace = 'svg' | 'mathml' | undefined
@@ -2524,6 +2525,7 @@ function baseCreateRenderer(
   return {
     render,
     hydrate,
+    hydrateNode,
     internals,
     createApp: createAppAPI(
       mountApp,
@@ -2639,7 +2641,10 @@ export function invalidateMount(hooks: LifecycleHook | undefined): void {
   }
 }
 
-function getVaporInterface(
+/**
+ * @internal
+ */
+export function getVaporInterface(
   instance: ComponentInternalInstance | null,
   vnode: VNode,
 ): VaporInteropInterface {
diff --git a/packages/runtime-dom/src/index.ts b/packages/runtime-dom/src/index.ts
index 51c72fe2ed1..0500110a4e0 100644
--- a/packages/runtime-dom/src/index.ts
+++ b/packages/runtime-dom/src/index.ts
@@ -319,7 +319,7 @@ export * from './jsx'
 /**
  * @internal
  */
-export { ensureRenderer, normalizeContainer }
+export { ensureRenderer, ensureHydrationRenderer, normalizeContainer }
 /**
  * @internal
  */
diff --git a/packages/runtime-vapor/__tests__/hydration.spec.ts b/packages/runtime-vapor/__tests__/hydration.spec.ts
index 6ba2bf895fb..fbc27f1d419 100644
--- a/packages/runtime-vapor/__tests__/hydration.spec.ts
+++ b/packages/runtime-vapor/__tests__/hydration.spec.ts
@@ -1,9 +1,16 @@
 import { createVaporSSRApp, delegateEvents } from '../src'
-import { nextTick, ref } from '@vue/runtime-dom'
+import { nextTick, reactive, ref } from '@vue/runtime-dom'
 import { compileScript, parse } from '@vue/compiler-sfc'
 import * as runtimeVapor from '../src'
 import * as runtimeDom from '@vue/runtime-dom'
 import * as VueServerRenderer from '@vue/server-renderer'
+import {
+  DYNAMIC_COMPONENT_ANCHOR_LABEL,
+  FOR_ANCHOR_LABEL,
+  IF_ANCHOR_LABEL,
+  SLOT_ANCHOR_LABEL,
+  isString,
+} from '@vue/shared'
 
 const Vue = { ...runtimeDom, ...runtimeVapor }
 
@@ -11,7 +18,7 @@ function compile(
   sfc: string,
   data: runtimeDom.Ref<any>,
   components: Record<string, any> = {},
-  ssr = false,
+  { vapor = true, ssr = false } = {},
 ) {
   if (!sfc.includes(`<script`)) {
     sfc =
@@ -25,7 +32,7 @@ function compile(
     isProd: true,
     inlineTemplate: true,
     genDefaultAs: '__sfc__',
-    vapor: true,
+    vapor,
     templateOptions: {
       ssr,
     },
@@ -47,19 +54,37 @@ function compile(
   )
 }
 
+async function testHydrationInterop(
+  code: string,
+  components?: Record<string, string | { code: string; vapor: boolean }>,
+  data?: any,
+) {
+  return testHydration(code, components, data, { interop: true, vapor: false })
+}
+
 async function testHydration(
   code: string,
-  components: Record<string, string> = {},
+  components: Record<string, string | { code: string; vapor: boolean }> = {},
+  data: any = ref('foo'),
+  { interop = false, vapor = true } = {},
 ) {
-  const data = ref('foo')
   const ssrComponents: any = {}
   const clientComponents: any = {}
   for (const key in components) {
-    clientComponents[key] = compile(components[key], data, clientComponents)
-    ssrComponents[key] = compile(components[key], data, ssrComponents, true)
+    const comp = components[key]
+    const code = isString(comp) ? comp : comp.code
+    const isVaporComp = isString(comp) || !!comp.vapor
+    clientComponents[key] = compile(code, data, clientComponents, {
+      vapor: isVaporComp,
+      ssr: false,
+    })
+    ssrComponents[key] = compile(code, data, ssrComponents, {
+      vapor: isVaporComp,
+      ssr: true,
+    })
   }
 
-  const serverComp = compile(code, data, ssrComponents, true)
+  const serverComp = compile(code, data, ssrComponents, { vapor, ssr: true })
   const html = await VueServerRenderer.renderToString(
     runtimeDom.createSSRApp(serverComp),
   )
@@ -67,8 +92,17 @@ async function testHydration(
   document.body.appendChild(container)
   container.innerHTML = html
 
-  const clientComp = compile(code, data, clientComponents)
-  const app = createVaporSSRApp(clientComp)
+  const clientComp = compile(code, data, clientComponents, {
+    vapor,
+    ssr: false,
+  })
+  let app
+  if (interop) {
+    app = runtimeDom.createSSRApp(clientComp)
+    app.use(runtimeVapor.vaporInteropPlugin)
+  } else {
+    app = createVaporSSRApp(clientComp)
+  }
   app.mount(container)
   return { data, container }
 }
@@ -78,72 +112,127 @@ const triggerEvent = (type: string, el: Element) => {
   el.dispatchEvent(event)
 }
 
-describe('Vapor Mode hydration', () => {
-  delegateEvents('click')
+delegateEvents('click')
 
-  beforeEach(() => {
-    document.body.innerHTML = ''
-  })
+beforeEach(() => {
+  document.body.innerHTML = ''
+})
 
-  test('root text', async () => {
-    const { data, container } = await testHydration(`
+describe('Vapor Mode hydration', () => {
+  describe('text', () => {
+    test('root text', async () => {
+      const { data, container } = await testHydration(`
       <template>{{ data }}</template>
     `)
-    expect(container.innerHTML).toMatchInlineSnapshot(`"foo"`)
+      expect(container.innerHTML).toMatchInlineSnapshot(`"foo"`)
 
-    data.value = 'bar'
-    await nextTick()
-    expect(container.innerHTML).toMatchInlineSnapshot(`"bar"`)
-  })
+      data.value = 'bar'
+      await nextTick()
+      expect(container.innerHTML).toMatchInlineSnapshot(`"bar"`)
+    })
 
-  test('root comment', async () => {
-    const { container } = await testHydration(`
-      <template><!----></template>
+    test('consecutive text nodes', async () => {
+      const { data, container } = await testHydration(`
+      <template>{{ data }}{{ data }}</template>
     `)
-    expect(container.innerHTML).toBe('<!---->')
-    expect(`Hydration children mismatch in <div>`).not.toHaveBeenWarned()
-  })
+      expect(container.innerHTML).toMatchInlineSnapshot(`"foofoo"`)
 
-  test('root with mixed element and text', async () => {
-    const { container, data } = await testHydration(`
-      <template> A<span>{{ data }}</span>{{ data }}</template>
+      data.value = 'bar'
+      await nextTick()
+      expect(container.innerHTML).toMatchInlineSnapshot(`"barbar"`)
+    })
+
+    test('consecutive text nodes with anchor insertion', async () => {
+      const { data, container } = await testHydration(`
+      <template><span/>{{ data }}{{ data }}<span/></template>
     `)
-    expect(container.innerHTML).toMatchInlineSnapshot(
-      `"<!--[--> A<span>foo</span>foo<!--]-->"`,
-    )
+      expect(container.innerHTML).toMatchInlineSnapshot(
+        `"<!--[--><span></span>foofoo<span></span><!--]-->"`,
+      )
+
+      data.value = 'bar'
+      await nextTick()
+      expect(container.innerHTML).toMatchInlineSnapshot(
+        `"<!--[--><span></span>barbar<span></span><!--]-->"`,
+      )
+    })
+
+    test('mixed text nodes', async () => {
+      const { data, container } = await testHydration(`
+      <template>{{ data }}A{{ data }}B{{ data }}</template>
+    `)
+      expect(container.innerHTML).toMatchInlineSnapshot(`"fooAfooBfoo"`)
 
-    data.value = 'bar'
-    await nextTick()
-    expect(container.innerHTML).toMatchInlineSnapshot(
-      `"<!--[--> A<span>bar</span>bar<!--]-->"`,
-    )
-  })
+      data.value = 'bar'
+      await nextTick()
+      expect(container.innerHTML).toMatchInlineSnapshot(`"barAbarBbar"`)
+    })
 
-  test('empty element', async () => {
-    const { container } = await testHydration(`
-      <template><div/></template>
+    test('mixed text nodes with anchor insertion', async () => {
+      const { data, container } = await testHydration(`
+      <template><span/>{{ data }}A{{ data }}B{{ data }}<span/></template>
     `)
-    expect(container.innerHTML).toBe('<div></div>')
-    expect(`Hydration children mismatch in <div>`).not.toHaveBeenWarned()
+      expect(container.innerHTML).toMatchInlineSnapshot(
+        `"<!--[--><span></span>fooAfooBfoo<span></span><!--]-->"`,
+      )
+
+      data.value = 'bar'
+      await nextTick()
+      expect(container.innerHTML).toMatchInlineSnapshot(
+        `"<!--[--><span></span>barAbarBbar<span></span><!--]-->"`,
+      )
+    })
   })
 
-  test('element with binding and text children', async () => {
-    const { container, data } = await testHydration(`
-      <template><div :class="data">{{ data }}</div></template>
+  describe('element', () => {
+    test('root comment', async () => {
+      const { container } = await testHydration(`
+      <template><!----></template>
     `)
-    expect(container.innerHTML).toMatchInlineSnapshot(
-      `"<div class="foo">foo</div>"`,
-    )
+      expect(container.innerHTML).toBe('<!---->')
+      expect(`Hydration children mismatch in <div>`).not.toHaveBeenWarned()
+    })
 
-    data.value = 'bar'
-    await nextTick()
-    expect(container.innerHTML).toMatchInlineSnapshot(
-      `"<div class="bar">bar</div>"`,
-    )
-  })
+    test('root with mixed element and text', async () => {
+      const { container, data } = await testHydration(`
+      <template> A<span>{{ data }}</span>{{ data }}</template>
+    `)
+      expect(container.innerHTML).toMatchInlineSnapshot(
+        `"<!--[--> A<span>foo</span>foo<!--]-->"`,
+      )
+
+      data.value = 'bar'
+      await nextTick()
+      expect(container.innerHTML).toMatchInlineSnapshot(
+        `"<!--[--> A<span>bar</span>bar<!--]-->"`,
+      )
+    })
+
+    test('empty element', async () => {
+      const { container } = await testHydration(`
+      <template><div/></template>
+    `)
+      expect(container.innerHTML).toBe('<div></div>')
+      expect(`Hydration children mismatch in <div>`).not.toHaveBeenWarned()
+    })
 
-  test('element with elements children', async () => {
-    const { container } = await testHydration(`
+    test('element with binding and text children', async () => {
+      const { container, data } = await testHydration(`
+      <template><div :class="data">{{ data }}</div></template>
+    `)
+      expect(container.innerHTML).toMatchInlineSnapshot(
+        `"<div class="foo">foo</div>"`,
+      )
+
+      data.value = 'bar'
+      await nextTick()
+      expect(container.innerHTML).toMatchInlineSnapshot(
+        `"<div class="bar">bar</div>"`,
+      )
+    })
+
+    test('element with elements children', async () => {
+      const { container } = await testHydration(`
       <template>
         <div>
           <span>{{ data }}</span>
@@ -151,99 +240,126 @@ describe('Vapor Mode hydration', () => {
         </div>
       </template>
     `)
-    expect(container.innerHTML).toMatchInlineSnapshot(
-      `"<div><span>foo</span><span class="foo"></span></div>"`,
-    )
-
-    // event handler
-    triggerEvent('click', container.querySelector('.foo')!)
+      expect(container.innerHTML).toMatchInlineSnapshot(
+        `"<div><span>foo</span><span class="foo"></span></div>"`,
+      )
+
+      // event handler
+      triggerEvent('click', container.querySelector('.foo')!)
+
+      await nextTick()
+      expect(container.innerHTML).toMatchInlineSnapshot(
+        `"<div><span>bar</span><span class="bar"></span></div>"`,
+      )
+    })
+
+    test('element with ref', async () => {
+      const { data, container } = await testHydration(
+        `<template>
+          <div ref="data">hi</div>
+        </template>
+      `,
+        {},
+        ref(null),
+      )
 
-    await nextTick()
-    expect(container.innerHTML).toMatchInlineSnapshot(
-      `"<div><span>bar</span><span class="bar"></span></div>"`,
-    )
+      expect(data.value).toBe(container.firstChild)
+    })
   })
 
-  test('basic component', async () => {
-    const { container, data } = await testHydration(
-      `
+  describe('component', () => {
+    test('basic component', async () => {
+      const { container, data } = await testHydration(
+        `
       <template><div><span></span><components.Child/></div></template>
       `,
-      { Child: `<template>{{ data }}</template>` },
-    )
-    expect(container.innerHTML).toMatchInlineSnapshot(
-      `"<div><span></span>foo</div>"`,
-    )
-
-    data.value = 'bar'
-    await nextTick()
-    expect(container.innerHTML).toMatchInlineSnapshot(
-      `"<div><span></span>bar</div>"`,
-    )
-  })
-
-  test('fragment component', async () => {
-    const { container, data } = await testHydration(
-      `
+        { Child: `<template>{{ data }}</template>` },
+      )
+      expect(container.innerHTML).toMatchInlineSnapshot(
+        `"<div><span></span>foo</div>"`,
+      )
+
+      data.value = 'bar'
+      await nextTick()
+      expect(container.innerHTML).toMatchInlineSnapshot(
+        `"<div><span></span>bar</div>"`,
+      )
+    })
+
+    test('fragment component', async () => {
+      const { container, data } = await testHydration(
+        `
       <template><div><span></span><components.Child/></div></template>
       `,
-      { Child: `<template><div>{{ data }}</div>-{{ data }}-</template>` },
-    )
-    expect(container.innerHTML).toMatchInlineSnapshot(
-      `"<div><span></span><!--[--><div>foo</div>-foo-<!--]--></div>"`,
-    )
-
-    data.value = 'bar'
-    await nextTick()
-    expect(container.innerHTML).toMatchInlineSnapshot(
-      `"<div><span></span><!--[--><div>bar</div>-bar-<!--]--></div>"`,
-    )
-  })
-
-  test('fragment component with prepend', async () => {
-    const { container, data } = await testHydration(
-      `
+        { Child: `<template><div>{{ data }}</div>-{{ data }}-</template>` },
+      )
+      expect(container.innerHTML).toMatchInlineSnapshot(
+        `"<div><span></span><!--[--><div>foo</div>-foo-<!--]--></div>"`,
+      )
+
+      data.value = 'bar'
+      await nextTick()
+      expect(container.innerHTML).toMatchInlineSnapshot(
+        `"<div><span></span><!--[--><div>bar</div>-bar-<!--]--></div>"`,
+      )
+    })
+
+    test('fragment component with prepend', async () => {
+      const { container, data } = await testHydration(
+        `
       <template><div><components.Child/><span></span></div></template>
       `,
-      { Child: `<template><div>{{ data }}</div>-{{ data }}-</template>` },
-    )
-    expect(container.innerHTML).toMatchInlineSnapshot(
-      `"<div><!--[--><div>foo</div>-foo-<!--]--><span></span></div>"`,
-    )
-
-    data.value = 'bar'
-    await nextTick()
-    expect(container.innerHTML).toMatchInlineSnapshot(
-      `"<div><!--[--><div>bar</div>-bar-<!--]--><span></span></div>"`,
-    )
-  })
-
-  test('nested fragment components', async () => {
-    const { container, data } = await testHydration(
-      `
+        { Child: `<template><div>{{ data }}</div>-{{ data }}-</template>` },
+      )
+      expect(container.innerHTML).toMatchInlineSnapshot(
+        `"<div><!--[--><div>foo</div>-foo-<!--]--><span></span></div>"`,
+      )
+
+      data.value = 'bar'
+      await nextTick()
+      expect(container.innerHTML).toMatchInlineSnapshot(
+        `"<div><!--[--><div>bar</div>-bar-<!--]--><span></span></div>"`,
+      )
+    })
+
+    test('nested fragment components', async () => {
+      const { container, data } = await testHydration(
+        `
       <template><div><components.Parent/><span></span></div></template>
       `,
-      {
-        Parent: `<template><div/><components.Child/><div/></template>`,
-        Child: `<template><div>{{ data }}</div>-{{ data }}-</template>`,
-      },
-    )
-    expect(container.innerHTML).toMatchInlineSnapshot(
-      `"<div><!--[--><div></div><!--[--><div>foo</div>-foo-<!--]--><div></div><!--]--><span></span></div>"`,
-    )
-
-    data.value = 'bar'
-    await nextTick()
-    expect(container.innerHTML).toMatchInlineSnapshot(
-      `"<div><!--[--><div></div><!--[--><div>bar</div>-bar-<!--]--><div></div><!--]--><span></span></div>"`,
-    )
-  })
-
-  // problem is the <!> placeholder does not exist in SSR output
-  test.todo('component with anchor insertion', async () => {
-    const { container, data } = await testHydration(
-      `
-      <template>
+        {
+          Parent: `<template><div/><components.Child/><div/></template>`,
+          Child: `<template><div>{{ data }}</div>-{{ data }}-</template>`,
+        },
+      )
+      expect(container.innerHTML).toBe(
+        `<div>` +
+          `<!--[-->` +
+          `<div></div>` +
+          `<!--[--><div>foo</div>-foo-<!--]-->` +
+          `<div></div>` +
+          `<!--]-->` +
+          `<span></span>` +
+          `</div>`,
+      )
+
+      data.value = 'bar'
+      await nextTick()
+      expect(container.innerHTML).toBe(
+        `<div>` +
+          `<!--[-->` +
+          `<div></div>` +
+          `<!--[--><div>bar</div>-bar-<!--]-->` +
+          `<div></div>` +
+          `<!--]-->` +
+          `<span></span>` +
+          `</div>`,
+      )
+    })
+
+    test('component with anchor insertion', async () => {
+      const { container, data } = await testHydration(
+        `<template>
         <div>
           <span/>
           <components.Child/>
@@ -251,20 +367,82 @@ describe('Vapor Mode hydration', () => {
         </div>
       </template>
       `,
-      {
-        Child: `<template>{{ data }}</template>`,
-      },
-    )
-    expect(container.innerHTML).toMatchInlineSnapshot()
-
-    data.value = 'bar'
-    await nextTick()
-    expect(container.innerHTML).toMatchInlineSnapshot()
-  })
-
-  test.todo('consecutive component with anchor insertion', async () => {
-    const { container, data } = await testHydration(
-      `<template>
+        {
+          Child: `<template>{{ data }}</template>`,
+        },
+      )
+      expect(container.innerHTML).toMatchInlineSnapshot(
+        `"<div><span></span>foo<span></span></div>"`,
+      )
+
+      data.value = 'bar'
+      await nextTick()
+      expect(container.innerHTML).toMatchInlineSnapshot(
+        `"<div><span></span>bar<span></span></div>"`,
+      )
+    })
+
+    test('nested components with anchor insertion', async () => {
+      const { container, data } = await testHydration(
+        `
+      <template><components.Parent/></template>
+      `,
+        {
+          Parent: `<template><div><span/><components.Child/><span/></div></template>`,
+          Child: `<template><div>{{ data }}</div></template>`,
+        },
+      )
+      expect(container.innerHTML).toBe(
+        `<div><span></span><div>foo</div><span></span></div>`,
+      )
+
+      data.value = 'bar'
+      await nextTick()
+      expect(container.innerHTML).toBe(
+        `<div><span></span><div>bar</div><span></span></div>`,
+      )
+    })
+
+    test('nested components with multi level anchor insertion', async () => {
+      const { container, data } = await testHydration(
+        `
+      <template><div><span></span><components.Parent/><span></span></div></template>
+      `,
+        {
+          Parent: `<template><div><span/><components.Child/><span/></div></template>`,
+          Child: `<template><div>{{ data }}</div></template>`,
+        },
+      )
+      expect(container.innerHTML).toBe(
+        `<div>` +
+          `<span></span>` +
+          `<div>` +
+          `<span></span>` +
+          `<div>foo</div>` +
+          `<span></span>` +
+          `</div>` +
+          `<span></span>` +
+          `</div>`,
+      )
+
+      data.value = 'bar'
+      await nextTick()
+      expect(container.innerHTML).toBe(
+        `<div>` +
+          `<span></span>` +
+          `<div>` +
+          `<span></span>` +
+          `<div>bar</div>` +
+          `<span></span>` +
+          `</div>` +
+          `<span></span>` +
+          `</div>`,
+      )
+    })
+
+    test('consecutive components with anchor insertion', async () => {
+      const { container, data } = await testHydration(
+        `<template>
         <div>
           <span/>
           <components.Child/>
@@ -273,31 +451,1921 @@ describe('Vapor Mode hydration', () => {
         </div>
       </template>
       `,
-      {
-        Child: `<template>{{ data }}</template>`,
-      },
-    )
-    expect(container.innerHTML).toMatchInlineSnapshot()
+        {
+          Child: `<template>{{ data }}</template>`,
+        },
+      )
+      expect(container.innerHTML).toBe(
+        `<div>` +
+          `<span></span>` +
+          `foo` +
+          `<!--[[-->foo<!--]]-->` +
+          `<span></span>` +
+          `</div>`,
+      )
+
+      data.value = 'bar'
+      await nextTick()
+      expect(container.innerHTML).toBe(
+        `<div>` +
+          `<span></span>` +
+          `bar` +
+          `<!--[[-->bar<!--]]-->` +
+          `<span></span>` +
+          `</div>`,
+      )
+    })
+
+    test('nested consecutive components with anchor insertion', async () => {
+      const { container, data } = await testHydration(
+        `
+      <template><components.Parent/></template>
+      `,
+        {
+          Parent: `<template><div><span/><components.Child/><components.Child/><span/></div></template>`,
+          Child: `<template><div>{{ data }}</div></template>`,
+        },
+      )
+      expect(container.innerHTML).toBe(
+        `<div>` +
+          `<span></span>` +
+          `<div>foo</div>` +
+          `<!--[[--><div>foo</div><!--]]-->` +
+          `<span></span>` +
+          `</div>`,
+      )
+
+      data.value = 'bar'
+      await nextTick()
+      expect(container.innerHTML).toBe(
+        `<div>` +
+          `<span></span>` +
+          `<div>bar</div>` +
+          `<!--[[--><div>bar</div><!--]]-->` +
+          `<span></span>` +
+          `</div>`,
+      )
+    })
+
+    test('nested consecutive components with multi level anchor insertion', async () => {
+      const { container, data } = await testHydration(
+        `
+      <template><div><span></span><components.Parent/><span></span></div></template>
+      `,
+        {
+          Parent: `<template><div><span/><components.Child/><components.Child/><span/></div></template>`,
+          Child: `<template><div>{{ data }}</div></template>`,
+        },
+      )
+      expect(container.innerHTML).toBe(
+        `<div>` +
+          `<span></span>` +
+          `<div>` +
+          `<span></span>` +
+          `<div>foo</div>` +
+          `<!--[[--><div>foo</div><!--]]-->` +
+          `<span></span>` +
+          `</div>` +
+          `<span></span>` +
+          `</div>`,
+      )
+
+      data.value = 'bar'
+      await nextTick()
+      expect(container.innerHTML).toBe(
+        `<div>` +
+          `<span></span>` +
+          `<div>` +
+          `<span></span>` +
+          `<div>bar</div>` +
+          `<!--[[--><div>bar</div><!--]]-->` +
+          `<span></span>` +
+          `</div>` +
+          `<span></span>` +
+          `</div>`,
+      )
+    })
+
+    test('mixed component and element with anchor insertion', async () => {
+      const { container, data } = await testHydration(
+        `<template>
+        <div>
+          <span/>
+          <components.Child/>
+          <span/>
+          <components.Child/>
+          <span/>
+        </div>
+      </template>
+      `,
+        {
+          Child: `<template>{{ data }}</template>`,
+        },
+      )
+      expect(container.innerHTML).toBe(
+        `<div>` +
+          `<span></span>` +
+          `foo` +
+          `<span></span>` +
+          `foo` +
+          `<span></span>` +
+          `</div>`,
+      )
+
+      data.value = 'bar'
+      await nextTick()
+      expect(container.innerHTML).toBe(
+        `<div>` +
+          `<span></span>` +
+          `bar` +
+          `<span></span>` +
+          `bar` +
+          `<span></span>` +
+          `</div>`,
+      )
+    })
+
+    test('mixed component and text with anchor insertion', async () => {
+      const { container, data } = await testHydration(
+        `<template>
+        <div>
+          <span/>
+          <components.Child/>
+          {{ data }}
+          <components.Child/>
+          <span/>
+        </div>
+      </template>
+      `,
+        {
+          Child: `<template>{{ data }}</template>`,
+        },
+      )
+      expect(container.innerHTML).toBe(
+        `<div>` +
+          `<span></span>` +
+          `foo` +
+          `<!--[[--> foo <!--]]-->` +
+          `foo` +
+          `<span></span>` +
+          `</div>`,
+      )
+
+      data.value = 'bar'
+      await nextTick()
+      expect(container.innerHTML).toBe(
+        `<div>` +
+          `<span></span>` +
+          `bar` +
+          `<!--[[--> bar <!--]]-->` +
+          `bar` +
+          `<span></span>` +
+          `</div>`,
+      )
+    })
+
+    test('fragment component with anchor insertion', async () => {
+      const { container, data } = await testHydration(
+        `<template>
+        <div>
+          <span/>
+          <components.Child/>
+          <span/>
+        </div>
+      </template>
+      `,
+        {
+          Child: `<template><div>{{ data }}</div>-{{ data }}</template>`,
+        },
+      )
+      expect(container.innerHTML).toBe(
+        `<div>` +
+          `<span></span>` +
+          `<!--[--><div>foo</div>-foo<!--]-->` +
+          `<span></span>` +
+          `</div>`,
+      )
+
+      data.value = 'bar'
+      await nextTick()
+      expect(container.innerHTML).toBe(
+        `<div>` +
+          `<span></span>` +
+          `<!--[--><div>bar</div>-bar<!--]-->` +
+          `<span></span>` +
+          `</div>`,
+      )
+    })
+
+    test('nested fragment component with anchor insertion', async () => {
+      const { container, data } = await testHydration(
+        `
+      <template><components.Parent/></template>
+      `,
+        {
+          Parent: `<template><div><span/><components.Child/><span/></div></template>`,
+          Child: `<template><div>{{ data }}</div>-{{ data }}-</template>`,
+        },
+      )
+      expect(container.innerHTML).toBe(
+        `<div>` +
+          `<span></span>` +
+          `<!--[--><div>foo</div>-foo-<!--]-->` +
+          `<span></span>` +
+          `</div>`,
+      )
+
+      data.value = 'bar'
+      await nextTick()
+      expect(container.innerHTML).toBe(
+        `<div>` +
+          `<span></span>` +
+          `<!--[--><div>bar</div>-bar-<!--]-->` +
+          `<span></span>` +
+          `</div>`,
+      )
+    })
+
+    test('nested fragment component with multi level anchor insertion', async () => {
+      const { container, data } = await testHydration(
+        `
+      <template><div><span/><components.Parent/><span/></div></template>
+      `,
+        {
+          Parent: `<template><div><span/><components.Child/><span/></div></template>`,
+          Child: `<template><div>{{ data }}</div>-{{ data }}-</template>`,
+        },
+      )
+      expect(container.innerHTML).toBe(
+        `<div>` +
+          `<span></span>` +
+          `<div>` +
+          `<span></span>` +
+          `<!--[--><div>foo</div>-foo-<!--]-->` +
+          `<span></span>` +
+          `</div>` +
+          `<span></span>` +
+          `</div>`,
+      )
+
+      data.value = 'bar'
+      await nextTick()
+      expect(container.innerHTML).toBe(
+        `<div>` +
+          `<span></span>` +
+          `<div>` +
+          `<span></span>` +
+          `<!--[--><div>bar</div>-bar-<!--]-->` +
+          `<span></span>` +
+          `</div>` +
+          `<span></span>` +
+          `</div>`,
+      )
+    })
+
+    test('consecutive fragment components with anchor insertion', async () => {
+      const { container, data } = await testHydration(
+        `<template>
+          <div>
+            <span/>
+            <components.Child/>
+            <components.Child/>
+            <span/>
+          </div>
+        </template>
+      `,
+        {
+          Child: `<template><div>{{ data }}</div>-{{ data }}</template>`,
+        },
+      )
+      expect(container.innerHTML).toBe(
+        `<div>` +
+          `<span></span>` +
+          `<!--[--><div>foo</div>-foo<!--]-->` +
+          `<!--[[-->` +
+          `<!--[--><div>foo</div>-foo<!--]-->` +
+          `<!--]]-->` +
+          `<span></span>` +
+          `</div>`,
+      )
+
+      data.value = 'bar'
+      await nextTick()
+      expect(container.innerHTML).toBe(
+        `<div>` +
+          `<span></span>` +
+          `<!--[--><div>bar</div>-bar<!--]-->` +
+          `<!--[[-->` +
+          `<!--[--><div>bar</div>-bar<!--]-->` +
+          `<!--]]-->` +
+          `<span></span>` +
+          `</div>`,
+      )
+    })
+
+    test('nested consecutive fragment components with anchor insertion', async () => {
+      const { container, data } = await testHydration(
+        `
+      <template><components.Parent/></template>
+      `,
+        {
+          Parent: `<template><div><span/><components.Child/><components.Child/><span/></div></template>`,
+          Child: `<template><div>{{ data }}</div>-{{ data }}-</template>`,
+        },
+      )
+      expect(container.innerHTML).toBe(
+        `<div>` +
+          `<span></span>` +
+          `<!--[--><div>foo</div>-foo-<!--]-->` +
+          `<!--[[-->` +
+          `<!--[--><div>foo</div>-foo-<!--]-->` +
+          `<!--]]-->` +
+          `<span></span>` +
+          `</div>`,
+      )
+
+      data.value = 'bar'
+      await nextTick()
+      expect(container.innerHTML).toBe(
+        `<div>` +
+          `<span></span>` +
+          `<!--[--><div>bar</div>-bar-<!--]-->` +
+          `<!--[[-->` +
+          `<!--[--><div>bar</div>-bar-<!--]-->` +
+          `<!--]]-->` +
+          `<span></span>` +
+          `</div>`,
+      )
+    })
+
+    test('nested consecutive fragment components with multi level anchor insertion', async () => {
+      const { container, data } = await testHydration(
+        `
+      <template><div><span></span><components.Parent/><span></span></div></template>
+      `,
+        {
+          Parent: `<template><div><span/><components.Child/><components.Child/><span/></div></template>`,
+          Child: `<template><div>{{ data }}</div>-{{ data }}-</template>`,
+        },
+      )
+      expect(container.innerHTML).toBe(
+        `<div>` +
+          `<span></span>` +
+          `<div>` +
+          `<span></span>` +
+          `<!--[--><div>foo</div>-foo-<!--]-->` +
+          `<!--[[-->` +
+          `<!--[--><div>foo</div>-foo-<!--]-->` +
+          `<!--]]-->` +
+          `<span></span>` +
+          `</div>` +
+          `<span></span>` +
+          `</div>`,
+      )
+
+      data.value = 'bar'
+      await nextTick()
+      expect(container.innerHTML).toBe(
+        `<div>` +
+          `<span></span>` +
+          `<div>` +
+          `<span></span>` +
+          `<!--[--><div>bar</div>-bar-<!--]-->` +
+          `<!--[[-->` +
+          `<!--[--><div>bar</div>-bar-<!--]-->` +
+          `<!--]]-->` +
+          `<span></span>` +
+          `</div>` +
+          `<span></span>` +
+          `</div>`,
+      )
+    })
+
+    test('nested consecutive fragment components with root level anchor insertion', async () => {
+      const { container, data } = await testHydration(
+        `
+      <template><div><span></span><components.Parent/><span></span></div></template>
+      `,
+        {
+          Parent: `<template><components.Child/><components.Child/></template>`,
+          Child: `<template><div>{{ data }}</div>-{{ data }}-</template>`,
+        },
+      )
+      expect(container.innerHTML).toBe(
+        `<div>` +
+          `<span></span>` +
+          `<!--[-->` +
+          `<!--[--><div>foo</div>-foo-<!--]-->` +
+          `<!--[--><div>foo</div>-foo-<!--]-->` +
+          `<!--]-->` +
+          `<span></span>` +
+          `</div>`,
+      )
+
+      data.value = 'bar'
+      await nextTick()
+      expect(container.innerHTML).toBe(
+        `<div>` +
+          `<span></span>` +
+          `<!--[-->` +
+          `<!--[--><div>bar</div>-bar-<!--]-->` +
+          `<!--[--><div>bar</div>-bar-<!--]-->` +
+          `<!--]-->` +
+          `<span></span>` +
+          `</div>`,
+      )
+    })
+
+    test('mixed fragment component and element with anchor insertion', async () => {
+      const { container, data } = await testHydration(
+        `<template>
+        <div>
+          <span/>
+          <components.Child/>
+          <span/>
+          <components.Child/>
+          <span/>
+        </div>
+      </template>
+      `,
+        {
+          Child: `<template><div>{{ data }}</div>-{{ data }}</template>`,
+        },
+      )
+      expect(container.innerHTML).toBe(
+        `<div>` +
+          `<span></span>` +
+          `<!--[--><div>foo</div>-foo<!--]-->` +
+          `<span></span>` +
+          `<!--[--><div>foo</div>-foo<!--]-->` +
+          `<span></span>` +
+          `</div>`,
+      )
+
+      data.value = 'bar'
+      await nextTick()
+      expect(container.innerHTML).toBe(
+        `<div>` +
+          `<span></span>` +
+          `<!--[--><div>bar</div>-bar<!--]-->` +
+          `<span></span>` +
+          `<!--[--><div>bar</div>-bar<!--]-->` +
+          `<span></span>` +
+          `</div>`,
+      )
+    })
+
+    test('mixed fragment component and text with anchor insertion', async () => {
+      const { container, data } = await testHydration(
+        `<template>
+        <div>
+          <span/>
+          <components.Child/>
+          {{ data }}
+          <components.Child/>
+          <span/>
+        </div>
+      </template>
+      `,
+        {
+          Child: `<template><div>{{ data }}</div>-{{ data }}</template>`,
+        },
+      )
+      expect(container.innerHTML).toBe(
+        `<div>` +
+          `<span></span>` +
+          `<!--[--><div>foo</div>-foo<!--]-->` +
+          ` <!--[[--> foo <!--]]--> ` +
+          `<!--[--><div>foo</div>-foo<!--]-->` +
+          `<span></span>` +
+          `</div>`,
+      )
+
+      data.value = 'bar'
+      await nextTick()
+      expect(container.innerHTML).toBe(
+        `<div>` +
+          `<span></span>` +
+          `<!--[--><div>bar</div>-bar<!--]-->` +
+          ` <!--[[--> bar <!--]]--> ` +
+          `<!--[--><div>bar</div>-bar<!--]-->` +
+          `<span></span>` +
+          `</div>`,
+      )
+    })
+  })
 
-    data.value = 'bar'
-    await nextTick()
-    expect(container.innerHTML).toMatchInlineSnapshot()
+  describe('dynamic component', () => {
+    const anchorLabel = DYNAMIC_COMPONENT_ANCHOR_LABEL
+
+    test('basic dynamic component', async () => {
+      const { container, data } = await testHydration(
+        `<template>
+          <component :is="components[data]"/>
+        </template>`,
+        {
+          foo: `<template><div>foo</div></template>`,
+          bar: `<template><div>bar</div></template>`,
+        },
+        ref('foo'),
+      )
+      expect(container.innerHTML).toBe(`<div>foo</div><!--${anchorLabel}-->`)
+
+      data.value = 'bar'
+      await nextTick()
+      expect(container.innerHTML).toBe(`<div>bar</div><!--${anchorLabel}-->`)
+    })
+
+    test('dynamic component with anchor insertion', async () => {
+      const { container, data } = await testHydration(
+        `<template>
+          <div>
+            <span/>
+            <component :is="components[data]"/>
+            <span/>
+          </div>
+        </template>`,
+        {
+          foo: `<template><div>foo</div></template>`,
+          bar: `<template><div>bar</div></template>`,
+        },
+        ref('foo'),
+      )
+      expect(container.innerHTML).toBe(
+        `<div>` +
+          `<span></span>` +
+          `<div>foo</div><!--${anchorLabel}-->` +
+          `<span></span>` +
+          `</div>`,
+      )
+
+      data.value = 'bar'
+      await nextTick()
+      expect(container.innerHTML).toBe(
+        `<div>` +
+          `<span></span>` +
+          `<div>bar</div><!--${anchorLabel}-->` +
+          `<span></span>` +
+          `</div>`,
+      )
+    })
+
+    test('consecutive dynamic components with anchor insertion', async () => {
+      const { container, data } = await testHydration(
+        `<template>
+          <div>
+            <span/>
+            <component :is="components[data]"/>
+            <component :is="components[data]"/>
+            <span/>
+          </div>
+        </template>`,
+        {
+          foo: `<template><div>foo</div></template>`,
+          bar: `<template><div>bar</div></template>`,
+        },
+        ref('foo'),
+      )
+      expect(container.innerHTML).toBe(
+        `<div>` +
+          `<span></span>` +
+          `<div>foo</div><!--${anchorLabel}-->` +
+          `<!--[[--><div>foo</div><!--${anchorLabel}--><!--]]-->` +
+          `<span></span>` +
+          `</div>`,
+      )
+
+      data.value = 'bar'
+      await nextTick()
+      expect(container.innerHTML).toBe(
+        `<div>` +
+          `<span></span>` +
+          `<div>bar</div><!--${anchorLabel}-->` +
+          `<!--[[--><div>bar</div><!--${anchorLabel}--><!--]]-->` +
+          `<span></span>` +
+          `</div>`,
+      )
+    })
   })
 
-  test.todo('if')
+  describe('if', () => {
+    const anchorLabel = IF_ANCHOR_LABEL
+
+    test('basic toggle - true -> false', async () => {
+      const data = ref(true)
+      const { container } = await testHydration(
+        `<template>
+          <div v-if="data">foo</div>
+        </template>`,
+        undefined,
+        data,
+      )
+      expect(container.innerHTML).toBe(`<div>foo</div><!--${anchorLabel}-->`)
+
+      data.value = false
+      await nextTick()
+      expect(container.innerHTML).toBe(`<!--${anchorLabel}-->`)
+    })
+
+    test('basic toggle - false -> true', async () => {
+      const data = ref(false)
+      const { container } = await testHydration(
+        `<template>
+          <div v-if="data">foo</div>
+        </template>`,
+        undefined,
+        data,
+      )
+      // v-if="false" is rendered as <!----> in the server-rendered HTML
+      // it reused as anchor, so the anchor label is empty
+      expect(container.innerHTML).toBe(`<!---->`)
+
+      data.value = true
+      await nextTick()
+      expect(container.innerHTML).toBe(`<div>foo</div><!---->`)
+    })
+
+    test('v-if/else-if/else chain - switch branches', async () => {
+      const data = ref('a')
+      const { container } = await testHydration(
+        `<template>
+          <div v-if="data === 'a'">foo</div>
+          <div v-else-if="data === 'b'">bar</div>
+          <div v-else>baz</div>
+        </template>`,
+        undefined,
+        data,
+      )
+      expect(container.innerHTML).toBe(`<div>foo</div><!--${anchorLabel}-->`)
+
+      data.value = 'b'
+      await nextTick()
+      expect(container.innerHTML).toBe(
+        `<div>bar</div><!--${anchorLabel}--><!--${anchorLabel}-->`,
+      )
+
+      data.value = 'c'
+      await nextTick()
+      expect(container.innerHTML).toBe(
+        `<div>baz</div><!--${anchorLabel}--><!--${anchorLabel}-->`,
+      )
+
+      data.value = 'a'
+      await nextTick()
+      expect(container.innerHTML).toBe(`<div>foo</div><!--${anchorLabel}-->`)
+    })
+
+    test('v-if/else-if/else chain - switch branches (PROD)', async () => {
+      try {
+        __DEV__ = false
+        const data = ref('a')
+        const { container } = await testHydration(
+          `<template>
+            <div v-if="data === 'a'">foo</div>
+            <div v-else-if="data === 'b'">bar</div>
+            <div v-else>baz</div>
+          </template>`,
+          undefined,
+          data,
+        )
+        expect(container.innerHTML).toBe(`<div>foo</div><!--${anchorLabel}-->`)
+
+        data.value = 'b'
+        await nextTick()
+        // In PROD, the anchor of v-else-if (DynamicFragment) is an empty text node,
+        // so it won't be rendered
+        expect(container.innerHTML).toBe(`<div>bar</div><!--${anchorLabel}-->`)
+
+        data.value = 'c'
+        await nextTick()
+        // same as above
+        expect(container.innerHTML).toBe(`<div>baz</div><!--${anchorLabel}-->`)
+
+        data.value = 'a'
+        await nextTick()
+        expect(container.innerHTML).toBe(`<div>foo</div><!--${anchorLabel}-->`)
+      } finally {
+        __DEV__ = true
+      }
+    })
+
+    test('nested if', async () => {
+      const data = reactive({ outer: true, inner: true })
+      const { container } = await testHydration(
+        `<template>
+          <div v-if="data.outer">
+            <span>outer</span>
+            <div v-if="data.inner">inner</div>
+          </div>
+        </template>`,
+        undefined,
+        data,
+      )
+      expect(container.innerHTML).toBe(
+        `<div>` +
+          `<span>outer</span>` +
+          `<div>inner</div><!--${anchorLabel}-->` +
+          `</div><!--${anchorLabel}-->`,
+      )
+
+      data.inner = false
+      await nextTick()
+      expect(container.innerHTML).toBe(
+        `<div>` +
+          `<span>outer</span>` +
+          `<!--${anchorLabel}-->` +
+          `</div><!--${anchorLabel}-->`,
+      )
+
+      data.outer = false
+      await nextTick()
+      expect(container.innerHTML).toBe(`<!--${anchorLabel}-->`)
+    })
+
+    test('on component', async () => {
+      const data = ref(true)
+      const { container } = await testHydration(
+        `<template>
+          <components.Child v-if="data"/>
+        </template>`,
+        { Child: `<template>foo</template>` },
+        data,
+      )
+      expect(container.innerHTML).toBe(`foo<!--${anchorLabel}-->`)
+
+      data.value = false
+      await nextTick()
+      expect(container.innerHTML).toBe(`<!--${anchorLabel}-->`)
+    })
+
+    test('v-if/else-if/else chain on component - switch branches', async () => {
+      const data = ref('a')
+      const { container } = await testHydration(
+        `<template>
+          <components.Child1 v-if="data === 'a'"/>
+          <components.Child2 v-else-if="data === 'b'"/>
+          <components.Child3 v-else/>
+        </template>`,
+        {
+          Child1: `<template><span>{{data}} child1</span></template>`,
+          Child2: `<template><span>{{data}} child2</span></template>`,
+          Child3: `<template><span>{{data}} child3</span></template>`,
+        },
+        data,
+      )
+      expect(container.innerHTML).toBe(
+        `<span>a child1</span><!--${anchorLabel}-->`,
+      )
+
+      data.value = 'b'
+      await nextTick()
+      expect(container.innerHTML).toBe(
+        `<span>b child2</span><!--${anchorLabel}--><!--${anchorLabel}-->`,
+      )
+
+      data.value = 'c'
+      await nextTick()
+      expect(container.innerHTML).toBe(
+        `<span>c child3</span><!--${anchorLabel}--><!--${anchorLabel}-->`,
+      )
+
+      data.value = 'a'
+      await nextTick()
+      expect(container.innerHTML).toBe(
+        `<span>a child1</span><!--${anchorLabel}-->`,
+      )
+    })
+
+    test('v-if/else-if/else chain on component - switch branches (PROD)', async () => {
+      try {
+        __DEV__ = false
+        const data = ref('a')
+        const { container } = await testHydration(
+          `<template>
+            <components.Child1 v-if="data === 'a'"/>
+            <components.Child2 v-else-if="data === 'b'"/>
+            <components.Child3 v-else/>
+          </template>`,
+          {
+            Child1: `<template><span>{{data}} child1</span></template>`,
+            Child2: `<template><span>{{data}} child2</span></template>`,
+            Child3: `<template><span>{{data}} child3</span></template>`,
+          },
+          data,
+        )
+        expect(container.innerHTML).toBe(
+          `<span>a child1</span><!--${anchorLabel}-->`,
+        )
+
+        data.value = 'b'
+        await nextTick()
+        // In PROD, the anchor of v-else-if (DynamicFragment) is an empty text node,
+        // so it won't be rendered
+        expect(container.innerHTML).toBe(
+          `<span>b child2</span><!--${anchorLabel}-->`,
+        )
+
+        data.value = 'c'
+        await nextTick()
+        // same as above
+        expect(container.innerHTML).toBe(
+          `<span>c child3</span><!--${anchorLabel}-->`,
+        )
+
+        data.value = 'a'
+        await nextTick()
+        expect(container.innerHTML).toBe(
+          `<span>a child1</span><!--${anchorLabel}-->`,
+        )
+      } finally {
+        __DEV__ = true
+      }
+    })
+
+    test('on component with anchor insertion', async () => {
+      const data = ref(true)
+      const { container } = await testHydration(
+        `<template>
+          <div>
+            <span/>
+            <components.Child v-if="data"/>
+            <span/>
+          </div>
+        </template>`,
+        { Child: `<template>foo</template>` },
+        data,
+      )
+      expect(container.innerHTML).toBe(
+        `<div>` +
+          `<span></span>` +
+          `foo<!--${anchorLabel}-->` +
+          `<span></span>` +
+          `</div>`,
+      )
+
+      data.value = false
+      await nextTick()
+      expect(container.innerHTML).toBe(
+        `<div>` +
+          `<span></span>` +
+          `<!--${anchorLabel}-->` +
+          `<span></span>` +
+          `</div>`,
+      )
+    })
+
+    test('consecutive v-if on component with anchor insertion', async () => {
+      const data = ref(true)
+      const { container } = await testHydration(
+        `<template>
+          <div>
+            <span/>
+            <components.Child v-if="data"/>
+            <components.Child v-if="data"/>
+            <span/>
+          </div>
+        </template>`,
+        { Child: `<template>foo</template>` },
+        data,
+      )
+      expect(container.innerHTML).toBe(
+        `<div>` +
+          `<span></span>` +
+          `foo<!--${anchorLabel}-->` +
+          `foo<!--${anchorLabel}-->` +
+          `<span></span>` +
+          `</div>`,
+      )
+
+      data.value = false
+      await nextTick()
+      expect(container.innerHTML).toBe(
+        `<div>` +
+          `<span></span>` +
+          `<!--${anchorLabel}-->` +
+          `<!--${anchorLabel}-->` +
+          `<span></span>` +
+          `</div>`,
+      )
+    })
+
+    test('on fragment component', async () => {
+      const data = ref(true)
+      const { container } = await testHydration(
+        `<template>
+          <div>
+            <components.Child v-if="data"/>
+          </div>
+        </template>`,
+        {
+          Child: `<template><div>{{ data }}</div>-{{ data }}-</template>`,
+        },
+        data,
+      )
+      expect(container.innerHTML).toBe(
+        `<div>` +
+          `<!--[--><div>true</div>-true-<!--]-->` +
+          `<!--if-->` +
+          `</div>`,
+      )
+
+      data.value = false
+      await nextTick()
+      expect(container.innerHTML).toBe(
+        `<div><!--[--><!--]--><!--${anchorLabel}--></div>`,
+      )
+    })
+
+    test('on fragment component with anchor insertion', async () => {
+      const data = ref(true)
+      const { container } = await testHydration(
+        `<template>
+          <div>
+            <span/>
+            <components.Child v-if="data"/>
+            <span/>
+          </div>
+        </template>`,
+        {
+          Child: `<template><div>{{ data }}</div>-{{ data }}-</template>`,
+        },
+        data,
+      )
+      expect(container.innerHTML).toBe(
+        `<div>` +
+          `<span></span>` +
+          `<!--[--><div>true</div>-true-<!--]-->` +
+          `<!--if-->` +
+          `<span></span>` +
+          `</div>`,
+      )
+
+      data.value = false
+      await nextTick()
+      expect(container.innerHTML).toBe(
+        `<div>` +
+          `<span></span>` +
+          `<!--[--><!--]-->` +
+          `<!--${anchorLabel}-->` +
+          `<span></span>` +
+          `</div>`,
+      )
+    })
+
+    test('consecutive v-if on fragment component with anchor insertion', async () => {
+      const data = ref(true)
+      const { container } = await testHydration(
+        `<template>
+          <div>
+            <span/>
+            <components.Child v-if="data"/>
+            <components.Child v-if="data"/>
+            <span/>
+          </div>
+        </template>`,
+        {
+          Child: `<template><div>{{ data }}</div>-{{ data }}-</template>`,
+        },
+        data,
+      )
+      expect(container.innerHTML).toBe(
+        `<div>` +
+          `<span></span>` +
+          `<!--[--><div>true</div>-true-<!--]--><!--${anchorLabel}-->` +
+          `<!--[--><div>true</div>-true-<!--]--><!--${anchorLabel}-->` +
+          `<span></span>` +
+          `</div>`,
+      )
+
+      data.value = false
+      await nextTick()
+      expect(container.innerHTML).toBe(
+        `<div>` +
+          `<span></span>` +
+          `<!--[--><!--]--><!--${anchorLabel}-->` +
+          `<!--[--><!--]--><!--${anchorLabel}-->` +
+          `<span></span>` +
+          `</div>`,
+      )
+    })
+
+    test('on dynamic component with anchor insertion', async () => {
+      const dynamicComponentAnchorLabel = DYNAMIC_COMPONENT_ANCHOR_LABEL
+      const data = ref(true)
+      const { container } = await testHydration(
+        `<template>
+          <div>
+            <span/>
+            <component :is="components.Child" v-if="data"/>
+            <span/>
+          </div>
+        </template>`,
+        { Child: `<template>foo</template>` },
+        data,
+      )
+      expect(container.innerHTML).toBe(
+        `<div>` +
+          `<span></span>` +
+          `foo<!--${dynamicComponentAnchorLabel}--><!--${anchorLabel}-->` +
+          `<span></span>` +
+          `</div>`,
+      )
+
+      data.value = false
+      await nextTick()
+      expect(container.innerHTML).toBe(
+        `<div>` +
+          `<span></span>` +
+          `<!--${anchorLabel}-->` +
+          `<span></span>` +
+          `</div>`,
+      )
+    })
+  })
 
-  test.todo('for')
+  describe('for', () => {
+    const forAnchorLabel = FOR_ANCHOR_LABEL
+    const slotAnchorLabel = SLOT_ANCHOR_LABEL
+
+    test('basic v-for', async () => {
+      const { container, data } = await testHydration(
+        `<template>
+          <div>
+            <span v-for="item in data" :key="item">{{ item }}</span>
+          </div>
+        </template>`,
+        undefined,
+        ref(['a', 'b', 'c']),
+      )
+      expect(container.innerHTML).toBe(
+        `<div>` +
+          `<!--[-->` +
+          `<span>a</span>` +
+          `<span>b</span>` +
+          `<span>c</span>` +
+          `<!--]--><!--${forAnchorLabel}-->` +
+          `</div>`,
+      )
+
+      data.value.push('d')
+      await nextTick()
+      expect(container.innerHTML).toBe(
+        `<div>` +
+          `<!--[-->` +
+          `<span>a</span>` +
+          `<span>b</span>` +
+          `<span>c</span>` +
+          `<!--]-->` +
+          `<span>d</span>` +
+          `<!--${forAnchorLabel}-->` +
+          `</div>`,
+      )
+    })
+
+    test('v-for with text node', async () => {
+      const { container, data } = await testHydration(
+        `<template>
+          <div>
+            <span v-for="item in data" :key="item">{{ item }}</span>
+          </div>
+        </template>`,
+        undefined,
+        ref(['a', 'b', 'c']),
+      )
+      expect(container.innerHTML).toBe(
+        `<div>` +
+          `<!--[-->` +
+          `<span>a</span><span>b</span><span>c</span>` +
+          `<!--]--><!--${forAnchorLabel}-->` +
+          `</div>`,
+      )
+
+      data.value.push('d')
+      await nextTick()
+      expect(container.innerHTML).toBe(
+        `<div>` +
+          `<!--[-->` +
+          `<span>a</span><span>b</span><span>c</span>` +
+          `<!--]-->` +
+          `<span>d</span>` +
+          `<!--${forAnchorLabel}-->` +
+          `</div>`,
+      )
+    })
+
+    test('v-for with anchor insertion', async () => {
+      const { container, data } = await testHydration(
+        `<template>
+          <div>
+            <span/>
+            <span v-for="item in data" :key="item">{{ item }}</span>
+            <span/>
+          </div>
+        </template>`,
+        undefined,
+        ref(['a', 'b', 'c']),
+      )
+      expect(container.innerHTML).toBe(
+        `<div>` +
+          `<span></span>` +
+          `<!--[-->` +
+          `<span>a</span>` +
+          `<span>b</span>` +
+          `<span>c</span>` +
+          `<!--]--><!--${forAnchorLabel}-->` +
+          `<span></span>` +
+          `</div>`,
+      )
+
+      data.value.push('d')
+      await nextTick()
+      expect(container.innerHTML).toBe(
+        `<div>` +
+          `<span></span>` +
+          `<!--[-->` +
+          `<span>a</span>` +
+          `<span>b</span>` +
+          `<span>c</span>` +
+          `<!--]-->` +
+          `<span>d</span>` +
+          `<!--${forAnchorLabel}-->` +
+          `<span></span>` +
+          `</div>`,
+      )
+
+      data.value.splice(0, 1)
+      await nextTick()
+      expect(container.innerHTML).toBe(
+        `<div>` +
+          `<span></span>` +
+          `<!--[-->` +
+          `<span>b</span>` +
+          `<span>c</span>` +
+          `<!--]-->` +
+          `<span>d</span>` +
+          `<!--${forAnchorLabel}-->` +
+          `<span></span>` +
+          `</div>`,
+      )
+    })
+
+    test('consecutive v-for with anchor insertion', async () => {
+      const { container, data } = await testHydration(
+        `<template>
+          <div>
+            <span/>
+            <span v-for="item in data" :key="item">{{ item }}</span>
+            <span v-for="item in data" :key="item">{{ item }}</span>
+            <span/>
+          </div>
+        </template>`,
+        undefined,
+        ref(['a', 'b', 'c']),
+      )
+      expect(container.innerHTML).toBe(
+        `<div>` +
+          `<span></span>` +
+          `<!--[-->` +
+          `<span>a</span>` +
+          `<span>b</span>` +
+          `<span>c</span>` +
+          `<!--]--><!--${forAnchorLabel}-->` +
+          `<!--[-->` +
+          `<span>a</span>` +
+          `<span>b</span>` +
+          `<span>c</span>` +
+          `<!--]--><!--${forAnchorLabel}-->` +
+          `<span></span>` +
+          `</div>`,
+      )
+
+      data.value.push('d')
+      await nextTick()
+      expect(container.innerHTML).toBe(
+        `<div>` +
+          `<span></span>` +
+          `<!--[-->` +
+          `<span>a</span>` +
+          `<span>b</span>` +
+          `<span>c</span>` +
+          `<!--]-->` +
+          `<span>d</span>` +
+          `<!--${forAnchorLabel}-->` +
+          `<!--[-->` +
+          `<span>a</span>` +
+          `<span>b</span>` +
+          `<span>c</span>` +
+          `<!--]-->` +
+          `<span>d</span>` +
+          `<!--${forAnchorLabel}-->` +
+          `<span></span>` +
+          `</div>`,
+      )
+
+      data.value.splice(0, 2)
+      await nextTick()
+      expect(container.innerHTML).toBe(
+        `<div>` +
+          `<span></span>` +
+          `<!--[-->` +
+          `<span>c</span>` +
+          `<!--]-->` +
+          `<span>d</span>` +
+          `<!--${forAnchorLabel}-->` +
+          `<!--[-->` +
+          `<span>c</span>` +
+          `<!--]-->` +
+          `<span>d</span>` +
+          `<!--${forAnchorLabel}-->` +
+          `<span></span>` +
+          `</div>`,
+      )
+    })
+
+    test('v-for on component', async () => {
+      const { container, data } = await testHydration(
+        `<template>
+          <div>
+            <components.Child v-for="item in data" :key="item"/>
+          </div>
+        </template>`,
+        {
+          Child: `<template><div>comp</div></template>`,
+        },
+        ref(['a', 'b', 'c']),
+      )
+
+      expect(container.innerHTML).toBe(
+        `<div>` +
+          `<!--[-->` +
+          `<div>comp</div>` +
+          `<div>comp</div>` +
+          `<div>comp</div>` +
+          `<!--]--><!--${forAnchorLabel}-->` +
+          `</div>`,
+      )
+
+      data.value.push('d')
+      await nextTick()
+      expect(container.innerHTML).toBe(
+        `<div>` +
+          `<!--[-->` +
+          `<div>comp</div>` +
+          `<div>comp</div>` +
+          `<div>comp</div>` +
+          `<!--]-->` +
+          `<div>comp</div>` +
+          `<!--${forAnchorLabel}-->` +
+          `</div>`,
+      )
+    })
+
+    test('v-for on component with slots', async () => {
+      const { container, data } = await testHydration(
+        `<template>
+          <div>
+            <components.Child v-for="item in data" :key="item">
+              <span>{{ item }}</span>
+            </components.Child>
+          </div>
+        </template>`,
+        {
+          Child: `<template><slot/></template>`,
+        },
+        ref(['a', 'b', 'c']),
+      )
+      expect(container.innerHTML).toBe(
+        `<div>` +
+          `<!--[-->` +
+          `<!--[--><span>a</span><!--]--><!--${slotAnchorLabel}-->` +
+          `<!--[--><span>b</span><!--]--><!--${slotAnchorLabel}-->` +
+          `<!--[--><span>c</span><!--]--><!--${slotAnchorLabel}-->` +
+          `<!--]--><!--${forAnchorLabel}-->` +
+          `</div>`,
+      )
+
+      data.value.push('d')
+      await nextTick()
+      expect(container.innerHTML).toBe(
+        `<div>` +
+          `<!--[-->` +
+          `<!--[--><span>a</span><!--]--><!--${slotAnchorLabel}-->` +
+          `<!--[--><span>b</span><!--]--><!--${slotAnchorLabel}-->` +
+          `<!--[--><span>c</span><!--]--><!--${slotAnchorLabel}-->` +
+          `<!--]-->` +
+          `<span>d</span><!--${slotAnchorLabel}-->` +
+          `<!--${forAnchorLabel}-->` +
+          `</div>`,
+      )
+    })
+
+    test('on fragment component', async () => {
+      const { container, data } = await testHydration(
+        `<template>
+          <div>
+            <components.Child v-for="item in data" :key="item"/>
+          </div>
+        </template>`,
+        {
+          Child: `<template><div>foo</div>-bar-</template>`,
+        },
+        ref(['a', 'b', 'c']),
+      )
+      expect(container.innerHTML).toBe(
+        `<div>` +
+          `<!--[-->` +
+          `<!--[--><div>foo</div>-bar-<!--]-->` +
+          `<!--[--><div>foo</div>-bar-<!--]-->` +
+          `<!--[--><div>foo</div>-bar-<!--]-->` +
+          `<!--]--><!--${forAnchorLabel}-->` +
+          `</div>`,
+      )
+
+      data.value.push('d')
+      await nextTick()
+      expect(container.innerHTML).toBe(
+        `<div>` +
+          `<!--[-->` +
+          `<!--[--><div>foo</div>-bar-<!--]-->` +
+          `<!--[--><div>foo</div>-bar-<!--]-->` +
+          `<!--[--><div>foo</div>-bar-<!--]-->` +
+          `<!--]-->` +
+          `<div>foo</div>-bar-` +
+          `<!--${forAnchorLabel}-->` +
+          `</div>`,
+      )
+    })
+  })
 
-  test.todo('slots')
+  describe('slots', () => {
+    const slotAnchorLabel = SLOT_ANCHOR_LABEL
+    const forAnchorLabel = FOR_ANCHOR_LABEL
+    test('basic slot', async () => {
+      const { data, container } = await testHydration(
+        `<template>
+          <components.Child>
+            <span>{{data}}</span>
+          </components.Child>
+        </template>`,
+        {
+          Child: `<template><slot/></template>`,
+        },
+      )
+      expect(container.innerHTML).toBe(
+        `<!--[--><span>foo</span><!--]--><!--${slotAnchorLabel}-->`,
+      )
+
+      data.value = 'bar'
+      await nextTick()
+      expect(container.innerHTML).toBe(
+        `<!--[--><span>bar</span><!--]--><!--${slotAnchorLabel}-->`,
+      )
+    })
+
+    test('named slot', async () => {
+      const { data, container } = await testHydration(
+        `<template>
+          <components.Child>
+            <template #foo>
+              <span>{{data}}</span>
+            </template>
+          </components.Child>
+        </template>`,
+        {
+          Child: `<template><slot name="foo"/></template>`,
+        },
+      )
+      expect(container.innerHTML).toBe(
+        `<!--[--><span>foo</span><!--]--><!--${slotAnchorLabel}-->`,
+      )
+
+      data.value = 'bar'
+      await nextTick()
+      expect(container.innerHTML).toBe(
+        `<!--[--><span>bar</span><!--]--><!--${slotAnchorLabel}-->`,
+      )
+    })
+
+    test('named slot with v-if', async () => {
+      const { data, container } = await testHydration(
+        `<template>
+          <components.Child>
+            <template #foo v-if="data">
+              <span>{{data}}</span>
+            </template>
+          </components.Child>
+        </template>`,
+        {
+          Child: `<template><slot name="foo"/></template>`,
+        },
+      )
+      expect(container.innerHTML).toBe(
+        `<!--[--><span>foo</span><!--]--><!--${slotAnchorLabel}-->`,
+      )
+
+      data.value = false
+      await nextTick()
+      expect(container.innerHTML).toBe(
+        `<!--[--><!--]--><!--${slotAnchorLabel}-->`,
+      )
+    })
+
+    test('named slot with v-if and v-for', async () => {
+      const data = reactive({
+        show: true,
+        items: ['a', 'b', 'c'],
+      })
+      const { container } = await testHydration(
+        `<template>
+          <components.Child>
+            <template #foo v-if="data.show">
+              <span v-for="item in data.items" :key="item">{{item}}</span>
+            </template>
+          </components.Child>
+        </template>`,
+        {
+          Child: `<template><slot name="foo"/></template>`,
+        },
+        data,
+      )
+      expect(container.innerHTML).toBe(
+        `<!--[-->` +
+          `<!--[--><span>a</span><span>b</span><span>c</span><!--]--><!--${forAnchorLabel}-->` +
+          `<!--]-->` +
+          `<!--${slotAnchorLabel}-->`,
+      )
+
+      data.show = false
+      await nextTick()
+      expect(container.innerHTML).toBe(
+        `<!--[--><!--[--><!--]--><!--]--><!--${slotAnchorLabel}-->`,
+      )
+    })
+
+    test('with anchor insertion', async () => {
+      const { data, container } = await testHydration(
+        `<template>
+          <components.Child>
+            <span/>
+            <span>{{data}}</span>
+            <span/>
+          </components.Child>
+        </template>`,
+        {
+          Child: `<template><slot/></template>`,
+        },
+      )
+      expect(container.innerHTML).toBe(
+        `<!--[-->` +
+          `<span></span>` +
+          `<span>foo</span>` +
+          `<span></span>` +
+          `<!--]-->` +
+          `<!--${slotAnchorLabel}-->`,
+      )
+
+      data.value = 'bar'
+      await nextTick()
+      expect(container.innerHTML).toBe(
+        `<!--[-->` +
+          `<span></span>` +
+          `<span>bar</span>` +
+          `<span></span>` +
+          `<!--]-->` +
+          `<!--${slotAnchorLabel}-->`,
+      )
+    })
+
+    test('with multi level anchor insertion', async () => {
+      const { data, container } = await testHydration(
+        `<template>
+          <components.Child>
+            <span/>
+            <span>{{data}}</span>
+            <span/>
+          </components.Child>
+        </template>`,
+        {
+          Child: `
+          <template>
+            <div/>
+              <div/>
+              <slot/>
+              <div/>
+            </div>
+          </template>`,
+        },
+      )
+      expect(container.innerHTML).toBe(
+        `<!--[-->` +
+          `<div></div>` +
+          `<div></div>` +
+          `<!--[-->` +
+          `<span></span>` +
+          `<span>foo</span>` +
+          `<span></span>` +
+          `<!--]-->` +
+          `<!--${slotAnchorLabel}-->` +
+          `<div></div>` +
+          `<!--]-->`,
+      )
+
+      data.value = 'bar'
+      await nextTick()
+      expect(container.innerHTML).toBe(
+        `<!--[-->` +
+          `<div></div>` +
+          `<div></div>` +
+          `<!--[-->` +
+          `<span></span>` +
+          `<span>bar</span>` +
+          `<span></span>` +
+          `<!--]-->` +
+          `<!--${slotAnchorLabel}-->` +
+          `<div></div>` +
+          `<!--]-->`,
+      )
+    })
+
+    test('mixed slot and text node', async () => {
+      const data = reactive({
+        text: 'foo',
+        msg: 'hi',
+      })
+      const { container } = await testHydration(
+        `<template>
+          <components.Child>
+            <span>{{data.text}}</span>
+          </components.Child>
+        </template>`,
+        {
+          Child: `<template><div><slot/>{{data.msg}}</div></template>`,
+        },
+        data,
+      )
+
+      expect(container.innerHTML).toBe(
+        `<div>` +
+          `<!--[--><span>foo</span><!--]--><!--${slotAnchorLabel}-->` +
+          `hi` +
+          `</div>`,
+      )
+
+      data.msg = 'bar'
+      await nextTick()
+      expect(container.innerHTML).toBe(
+        `<div>` +
+          `<!--[--><span>foo</span><!--]--><!--${slotAnchorLabel}-->` +
+          `bar` +
+          `</div>`,
+      )
+    })
+
+    test('mixed root slot and text node', async () => {
+      const data = reactive({
+        text: 'foo',
+        msg: 'hi',
+      })
+      const { container } = await testHydration(
+        `<template>
+          <components.Child>
+            <span>{{data.text}}</span>
+          </components.Child>
+        </template>`,
+        {
+          Child: `<template>{{data.text}}<slot/>{{data.msg}}</template>`,
+        },
+        data,
+      )
+
+      expect(container.innerHTML).toBe(
+        `<!--[-->` +
+          `foo` +
+          `<!--[--><span>foo</span><!--]--><!--${slotAnchorLabel}-->` +
+          `hi` +
+          `<!--]-->`,
+      )
+
+      data.msg = 'bar'
+      await nextTick()
+      expect(container.innerHTML).toBe(
+        `<!--[-->` +
+          `foo` +
+          `<!--[--><span>foo</span><!--]--><!--${slotAnchorLabel}-->` +
+          `bar` +
+          `<!--]-->`,
+      )
+    })
+
+    test('mixed slot and element', async () => {
+      const data = reactive({
+        text: 'foo',
+        msg: 'hi',
+      })
+      const { container } = await testHydration(
+        `<template>
+          <components.Child>
+            <span>{{data.text}}</span>
+          </components.Child>
+        </template>`,
+        {
+          Child: `<template><div><slot/><div>{{data.msg}}</div></div></template>`,
+        },
+        data,
+      )
+
+      expect(container.innerHTML).toBe(
+        `<div>` +
+          `<!--[--><span>foo</span><!--]--><!--${slotAnchorLabel}-->` +
+          `<div>hi</div>` +
+          `</div>`,
+      )
+
+      data.msg = 'bar'
+      await nextTick()
+      expect(container.innerHTML).toBe(
+        `<div>` +
+          `<!--[--><span>foo</span><!--]--><!--${slotAnchorLabel}-->` +
+          `<div>bar</div>` +
+          `</div>`,
+      )
+    })
+
+    test('mixed slot and component', async () => {
+      const data = reactive({
+        msg1: 'foo',
+        msg2: 'bar',
+      })
+      const { container } = await testHydration(
+        `<template>
+          <components.Child>
+            <span>{{data.msg1}}</span>
+          </components.Child>
+        </template>`,
+        {
+          Child: `
+          <template>
+            <div>
+              <components.Child2/>
+              <slot/>
+              <components.Child2/>
+            </div>
+          </template>`,
+          Child2: `
+          <template>
+            <div>{{data.msg2}}</div>
+          </template>`,
+        },
+        data,
+      )
+      expect(container.innerHTML).toBe(
+        `<div>` +
+          `<div>bar</div>` +
+          `<!--[--><span>foo</span><!--]--><!--${slotAnchorLabel}-->` +
+          `<div>bar</div>` +
+          `</div>`,
+      )
+
+      data.msg2 = 'hello'
+      await nextTick()
+      expect(container.innerHTML).toBe(
+        `<div>` +
+          `<div>hello</div>` +
+          `<!--[--><span>foo</span><!--]--><!--${slotAnchorLabel}-->` +
+          `<div>hello</div>` +
+          `</div>`,
+      )
+    })
+
+    test('mixed slot and fragment component', async () => {
+      const data = reactive({
+        msg1: 'foo',
+        msg2: 'bar',
+      })
+      const { container } = await testHydration(
+        `<template>
+          <components.Child>
+            <span>{{data.msg1}}</span>
+          </components.Child>
+        </template>`,
+        {
+          Child: `
+          <template>
+            <div>
+              <components.Child2/>
+              <slot/>
+              <components.Child2/>
+            </div>
+          </template>`,
+          Child2: `
+          <template>
+            <div>{{data.msg1}}</div> {{data.msg2}}
+          </template>`,
+        },
+        data,
+      )
+      expect(container.innerHTML).toBe(
+        `<div>` +
+          `<!--[--><div>foo</div> bar<!--]-->` +
+          `<!--[--><span>foo</span><!--]--><!--${slotAnchorLabel}-->` +
+          `<!--[--><div>foo</div> bar<!--]-->` +
+          `</div>`,
+      )
+
+      data.msg1 = 'hello'
+      data.msg2 = 'vapor'
+      await nextTick()
+      expect(container.innerHTML).toBe(
+        `<div>` +
+          `<!--[--><div>hello</div> vapor<!--]-->` +
+          `<!--[--><span>hello</span><!--]--><!--${slotAnchorLabel}-->` +
+          `<!--[--><div>hello</div> vapor<!--]-->` +
+          `</div>`,
+      )
+    })
+
+    test('mixed slot and v-if', async () => {
+      const data = reactive({
+        show: true,
+        msg: 'foo',
+      })
+      const { container } = await testHydration(
+        `<template>
+          <components.Child>
+            <span>{{data.msg}}</span>
+          </components.Child>
+        </template>`,
+        {
+          Child: `
+          <template>
+            <div v-if="data.show">{{data.msg}}</div>
+            <slot/>
+            <div v-if="data.show">{{data.msg}}</div>
+          </template>`,
+        },
+        data,
+      )
+
+      expect(container.innerHTML).toBe(
+        `<!--[-->` +
+          `<div>foo</div><!--if-->` +
+          `<!--[--><span>foo</span><!--]--><!--${slotAnchorLabel}-->` +
+          `<div>foo</div><!--if-->` +
+          `<!--]-->`,
+      )
+
+      data.show = false
+      await nextTick()
+      expect(container.innerHTML).toBe(
+        `<!--[-->` +
+          `<!--if-->` +
+          `<!--[--><span>foo</span><!--]--><!--${slotAnchorLabel}-->` +
+          `<!--if-->` +
+          `<!--]-->`,
+      )
+    })
+
+    test('mixed slot and v-for', async () => {
+      const data = reactive({
+        items: ['a', 'b', 'c'],
+        msg: 'foo',
+      })
+      const { container } = await testHydration(
+        `<template>
+          <components.Child>
+            <span>{{data.msg}}</span>
+          </components.Child>
+        </template>`,
+        {
+          Child: `
+          <template>
+            <div v-for="item in data.items" :key="item">{{item}}</div>
+            <slot/>
+            <div v-for="item in data.items" :key="item">{{item}}</div>
+          </template>`,
+        },
+        data,
+      )
+
+      expect(container.innerHTML).toBe(
+        `<!--[-->` +
+          `<!--[--><div>a</div><div>b</div><div>c</div><!--]--><!--${forAnchorLabel}-->` +
+          `<!--[--><span>foo</span><!--]--><!--${slotAnchorLabel}-->` +
+          `<!--[--><div>a</div><div>b</div><div>c</div><!--]--><!--${forAnchorLabel}-->` +
+          `<!--]-->`,
+      )
+
+      data.items.push('d')
+      await nextTick()
+      expect(container.innerHTML).toBe(
+        `<!--[-->` +
+          `<!--[--><div>a</div><div>b</div><div>c</div><!--]--><div>d</div><!--${forAnchorLabel}-->` +
+          `<!--[--><span>foo</span><!--]--><!--${slotAnchorLabel}-->` +
+          `<!--[--><div>a</div><div>b</div><div>c</div><!--]--><div>d</div><!--${forAnchorLabel}-->` +
+          `<!--]-->`,
+      )
+    })
+
+    test('consecutive slots', async () => {
+      const data = reactive({
+        msg1: 'foo',
+        msg2: 'bar',
+      })
+
+      const { container } = await testHydration(
+        `<template>
+          <components.Child>
+            <span>{{data.msg1}}</span>
+            <template #bar>
+              <span>{{data.msg2}}</span>
+            </template>
+          </components.Child>
+        </template>`,
+        {
+          Child: `<template><slot/><slot name="bar"/></template>`,
+        },
+        data,
+      )
+
+      expect(container.innerHTML).toBe(
+        `<!--[-->` +
+          `<!--[--><span>foo</span><!--]--><!--${slotAnchorLabel}-->` +
+          `<!--[--><span>bar</span><!--]--><!--${slotAnchorLabel}-->` +
+          `<!--]-->`,
+      )
+
+      data.msg1 = 'hello'
+      data.msg2 = 'vapor'
+      await nextTick()
+      expect(container.innerHTML).toBe(
+        `<!--[-->` +
+          `<!--[--><span>hello</span><!--]--><!--${slotAnchorLabel}-->` +
+          `<!--[--><span>vapor</span><!--]--><!--${slotAnchorLabel}-->` +
+          `<!--]-->`,
+      )
+    })
+
+    test('consecutive slots with anchor insertion', async () => {
+      const data = reactive({
+        msg1: 'foo',
+        msg2: 'bar',
+      })
+
+      const { container } = await testHydration(
+        `<template>
+          <components.Child>
+            <span>{{data.msg1}}</span>
+            <template #bar>
+              <span>{{data.msg2}}</span>
+            </template>
+          </components.Child>
+        </template>`,
+        {
+          Child: `<template>
+            <div>
+              <span/>
+              <slot/>
+              <slot name="bar"/>
+              <span/>
+            </div>
+          </template>`,
+        },
+        data,
+      )
+
+      expect(container.innerHTML).toBe(
+        `<div>` +
+          `<span></span>` +
+          `<!--[--><span>foo</span><!--]--><!--${slotAnchorLabel}-->` +
+          `<!--[--><span>bar</span><!--]--><!--${slotAnchorLabel}-->` +
+          `<span></span>` +
+          `</div>`,
+      )
+
+      data.msg1 = 'hello'
+      data.msg2 = 'vapor'
+      await nextTick()
+      expect(container.innerHTML).toBe(
+        `<div>` +
+          `<span></span>` +
+          `<!--[--><span>hello</span><!--]--><!--${slotAnchorLabel}-->` +
+          `<!--[--><span>vapor</span><!--]--><!--${slotAnchorLabel}-->` +
+          `<span></span>` +
+          `</div>`,
+      )
+    })
+  })
 
-  // test('element with ref', () => {
-  //   const el = ref()
-  //   const { vnode, container } = mountWithHydration('<div></div>', () =>
-  //     h('div', { ref: el }),
-  //   )
-  //   expect(vnode.el).toBe(container.firstChild)
-  //   expect(el.value).toBe(vnode.el)
-  // })
+  describe.todo('transition', async () => {
+    test('transition appear', async () => {})
+    test('transition appear with v-if', async () => {})
+    test('transition appear with v-show', async () => {})
+    test('transition appear w/ event listener', async () => {})
+  })
+
+  describe.todo('async component')
+
+  describe.todo('data-allow-mismatch')
 
   // test('with data-allow-mismatch component when using onServerPrefetch', async () => {
   //   const Comp = {
@@ -1791,3 +3859,90 @@ describe('Vapor Mode hydration', () => {
   test.todo('Teleport')
   test.todo('Suspense')
 })
+
+describe('VDOM hydration interop', () => {
+  test('basic vapor component', async () => {
+    const data = ref(true)
+    const { container } = await testHydrationInterop(
+      `<script setup>const data = _data; const components = _components;</script>
+      <template>
+        <components.VaporChild/>
+      </template>`,
+      {
+        VaporChild: {
+          code: `<template>{{ data }}</template>`,
+          vapor: true,
+        },
+      },
+      data,
+    )
+
+    expect(container.innerHTML).toMatchInlineSnapshot(`"true"`)
+
+    data.value = false
+    await nextTick()
+    expect(container.innerHTML).toMatchInlineSnapshot(`"false"`)
+  })
+
+  test('nested components (VDOM -> Vapor -> VDOM)', async () => {
+    const data = ref(true)
+    const { container } = await testHydrationInterop(
+      `<script setup>const data = _data; const components = _components;</script>
+      <template>
+        <components.VaporChild/>
+      </template>`,
+      {
+        VaporChild: {
+          code: `<template><components.VdomChild/></template>`,
+          vapor: true,
+        },
+        VdomChild: {
+          code: `<script setup>const data = _data;</script>
+            <template>{{ data }}</template>`,
+          vapor: false,
+        },
+      },
+      data,
+    )
+
+    expect(container.innerHTML).toMatchInlineSnapshot(`"true"`)
+
+    data.value = false
+    await nextTick()
+    expect(container.innerHTML).toMatchInlineSnapshot(`"false"`)
+  })
+
+  test('vapor slot render vdom component', async () => {
+    const data = ref(true)
+    const { container } = await testHydrationInterop(
+      `<script setup>const data = _data; const components = _components;</script>
+      <template>
+        <components.VaporChild>
+          <components.VdomChild/>
+        </components.VaporChild>
+      </template>`,
+      {
+        VaporChild: {
+          code: `<template><div><slot/></div></template>`,
+          vapor: true,
+        },
+        VdomChild: {
+          code: `<script setup>const data = _data;</script>
+            <template>{{ data }}</template>`,
+          vapor: false,
+        },
+      },
+      data,
+    )
+
+    expect(container.innerHTML).toMatchInlineSnapshot(
+      `"<div><!--[-->true<!--]--><!--slot--></div>"`,
+    )
+
+    data.value = false
+    await nextTick()
+    expect(container.innerHTML).toMatchInlineSnapshot(
+      `"<div><!--[-->false<!--]--><!--slot--></div>"`,
+    )
+  })
+})
diff --git a/packages/runtime-vapor/src/apiCreateDynamicComponent.ts b/packages/runtime-vapor/src/apiCreateDynamicComponent.ts
index 2126611d718..7ae689a0aee 100644
--- a/packages/runtime-vapor/src/apiCreateDynamicComponent.ts
+++ b/packages/runtime-vapor/src/apiCreateDynamicComponent.ts
@@ -1,9 +1,16 @@
 import { resolveDynamicComponent } from '@vue/runtime-dom'
-import { DynamicFragment, type VaporFragment } from './block'
+import { DynamicFragment, type VaporFragment, insert } from './block'
 import { createComponentWithFallback } from './component'
 import { renderEffect } from './renderEffect'
 import type { RawProps } from './componentProps'
 import type { RawSlots } from './componentSlots'
+import {
+  insertionAnchor,
+  insertionParent,
+  resetInsertionState,
+} from './insertionState'
+import { isHydrating } from './dom/hydration'
+import { DYNAMIC_COMPONENT_ANCHOR_LABEL } from '@vue/shared'
 
 export function createDynamicComponent(
   getter: () => any,
@@ -11,9 +18,14 @@ export function createDynamicComponent(
   rawSlots?: RawSlots | null,
   isSingleRoot?: boolean,
 ): VaporFragment {
-  const frag = __DEV__
-    ? new DynamicFragment('dynamic-component')
-    : new DynamicFragment()
+  const _insertionParent = insertionParent
+  const _insertionAnchor = insertionAnchor
+  if (!isHydrating) resetInsertionState()
+
+  const frag =
+    isHydrating || __DEV__
+      ? new DynamicFragment(DYNAMIC_COMPONENT_ANCHOR_LABEL)
+      : new DynamicFragment()
   renderEffect(() => {
     const value = getter()
     frag.update(
@@ -27,5 +39,9 @@ export function createDynamicComponent(
       value,
     )
   })
+
+  if (!isHydrating && _insertionParent) {
+    insert(frag, _insertionParent, _insertionAnchor)
+  }
   return frag
 }
diff --git a/packages/runtime-vapor/src/apiCreateFor.ts b/packages/runtime-vapor/src/apiCreateFor.ts
index 0cd8317532f..7138d01a6af 100644
--- a/packages/runtime-vapor/src/apiCreateFor.ts
+++ b/packages/runtime-vapor/src/apiCreateFor.ts
@@ -9,7 +9,13 @@ import {
   shallowRef,
   toReactive,
 } from '@vue/reactivity'
-import { getSequence, isArray, isObject, isString } from '@vue/shared'
+import {
+  FOR_ANCHOR_LABEL,
+  getSequence,
+  isArray,
+  isObject,
+  isString,
+} from '@vue/shared'
 import { createComment, createTextNode } from './dom/node'
 import {
   type Block,
@@ -22,8 +28,17 @@ import { currentInstance, isVaporComponent } from './component'
 import type { DynamicSlot } from './componentSlots'
 import { renderEffect } from './renderEffect'
 import { VaporVForFlags } from '../../shared/src/vaporFlags'
-import { isHydrating, locateHydrationNode } from './dom/hydration'
-import { insertionAnchor, insertionParent } from './insertionState'
+import {
+  currentHydrationNode,
+  isHydrating,
+  locateHydrationNode,
+  locateVaporFragmentAnchor,
+} from './dom/hydration'
+import {
+  insertionAnchor,
+  insertionParent,
+  resetInsertionState,
+} from './insertionState'
 
 class ForBlock extends VaporFragment {
   scope: EffectScope | undefined
@@ -71,15 +86,29 @@ export const createFor = (
   const _insertionParent = insertionParent
   const _insertionAnchor = insertionAnchor
   if (isHydrating) {
-    locateHydrationNode()
+    locateHydrationNode(true)
+  } else {
+    resetInsertionState()
   }
 
   let isMounted = false
   let oldBlocks: ForBlock[] = []
   let newBlocks: ForBlock[]
   let parent: ParentNode | undefined | null
-  // TODO handle this in hydration
-  const parentAnchor = __DEV__ ? createComment('for') : createTextNode()
+  let parentAnchor: Node
+  if (isHydrating) {
+    parentAnchor = locateVaporFragmentAnchor(
+      currentHydrationNode!,
+      FOR_ANCHOR_LABEL,
+    )!
+    if (__DEV__ && !parentAnchor) {
+      // this should not happen
+      throw new Error(`v-for fragment anchor node was not found.`)
+    }
+  } else {
+    parentAnchor = __DEV__ ? createComment('for') : createTextNode()
+  }
+
   const frag = new VaporFragment(oldBlocks)
   const instance = currentInstance!
   const canUseFastRemove = flags & VaporVForFlags.FAST_REMOVE
diff --git a/packages/runtime-vapor/src/apiCreateIf.ts b/packages/runtime-vapor/src/apiCreateIf.ts
index 71bfa32d5d3..3e370592b32 100644
--- a/packages/runtime-vapor/src/apiCreateIf.ts
+++ b/packages/runtime-vapor/src/apiCreateIf.ts
@@ -1,6 +1,11 @@
+import { IF_ANCHOR_LABEL } from '@vue/shared'
 import { type Block, type BlockFn, DynamicFragment, insert } from './block'
-import { isHydrating, locateHydrationNode } from './dom/hydration'
-import { insertionAnchor, insertionParent } from './insertionState'
+import { isHydrating } from './dom/hydration'
+import {
+  insertionAnchor,
+  insertionParent,
+  resetInsertionState,
+} from './insertionState'
 import { renderEffect } from './renderEffect'
 
 export function createIf(
@@ -11,15 +16,16 @@ export function createIf(
 ): Block {
   const _insertionParent = insertionParent
   const _insertionAnchor = insertionAnchor
-  if (isHydrating) {
-    locateHydrationNode()
-  }
+  if (!isHydrating) resetInsertionState()
 
   let frag: Block
   if (once) {
     frag = condition() ? b1() : b2 ? b2() : []
   } else {
-    frag = __DEV__ ? new DynamicFragment('if') : new DynamicFragment()
+    frag =
+      isHydrating || __DEV__
+        ? new DynamicFragment(IF_ANCHOR_LABEL)
+        : new DynamicFragment()
     renderEffect(() => (frag as DynamicFragment).update(condition() ? b1 : b2))
   }
 
diff --git a/packages/runtime-vapor/src/block.ts b/packages/runtime-vapor/src/block.ts
index b782afd38d3..d6be89efc39 100644
--- a/packages/runtime-vapor/src/block.ts
+++ b/packages/runtime-vapor/src/block.ts
@@ -7,7 +7,13 @@ import {
 } from './component'
 import { createComment, createTextNode } from './dom/node'
 import { EffectScope, pauseTracking, resetTracking } from '@vue/reactivity'
-import { isHydrating } from './dom/hydration'
+import {
+  currentHydrationNode,
+  isComment,
+  isHydrating,
+  locateHydrationNode,
+  locateVaporFragmentAnchor,
+} from './dom/hydration'
 
 export type Block =
   | Node
@@ -30,15 +36,20 @@ export class VaporFragment {
 }
 
 export class DynamicFragment extends VaporFragment {
-  anchor: Node
+  anchor!: Node
   scope: EffectScope | undefined
   current?: BlockFn
   fallback?: BlockFn
 
   constructor(anchorLabel?: string) {
     super([])
-    this.anchor =
-      __DEV__ && anchorLabel ? createComment(anchorLabel) : createTextNode()
+    if (isHydrating) {
+      locateHydrationNode(true)
+      this.hydrate(anchorLabel!)
+    } else {
+      this.anchor =
+        __DEV__ && anchorLabel ? createComment(anchorLabel) : createTextNode()
+    }
   }
 
   update(render?: BlockFn, key: any = render): void {
@@ -75,6 +86,22 @@ export class DynamicFragment extends VaporFragment {
 
     resetTracking()
   }
+
+  hydrate(label: string): void {
+    // for `v-if="false"` the node will be an empty comment, use it as the anchor.
+    // otherwise, find next sibling vapor fragment anchor
+    if (isComment(currentHydrationNode!, '')) {
+      this.anchor = currentHydrationNode
+    } else {
+      const anchor = locateVaporFragmentAnchor(currentHydrationNode!, label)!
+      if (anchor) {
+        this.anchor = anchor
+      } else if (__DEV__) {
+        // this should not happen
+        throw new Error(`${label} fragment anchor node was not found.`)
+      }
+    }
+  }
 }
 
 export function isFragment(val: NonNullable<unknown>): val is VaporFragment {
@@ -126,7 +153,6 @@ export function insert(
   } else {
     // fragment
     if (block.insert) {
-      // TODO handle hydration for vdom interop
       block.insert(parent, anchor)
     } else {
       insert(block.nodes, parent, anchor)
diff --git a/packages/runtime-vapor/src/component.ts b/packages/runtime-vapor/src/component.ts
index 548babebf8b..e0396aa2e71 100644
--- a/packages/runtime-vapor/src/component.ts
+++ b/packages/runtime-vapor/src/component.ts
@@ -59,7 +59,11 @@ import {
 } from './componentSlots'
 import { hmrReload, hmrRerender } from './hmr'
 import { isHydrating, locateHydrationNode } from './dom/hydration'
-import { insertionAnchor, insertionParent } from './insertionState'
+import {
+  insertionAnchor,
+  insertionParent,
+  resetInsertionState,
+} from './insertionState'
 
 export { currentInstance } from '@vue/runtime-dom'
 
@@ -142,6 +146,8 @@ export function createComponent(
   const _insertionAnchor = insertionAnchor
   if (isHydrating) {
     locateHydrationNode()
+  } else {
+    resetInsertionState()
   }
 
   // vdom interop enabled and component is not an explicit vapor component
@@ -151,7 +157,9 @@ export function createComponent(
       rawProps,
       rawSlots,
     )
-    if (!isHydrating && _insertionParent) {
+
+    // `frag.insert` handles both hydration and mounting
+    if (_insertionParent) {
       insert(frag, _insertionParent, _insertionAnchor)
     }
     return frag
@@ -270,7 +278,7 @@ export function createComponent(
   onScopeDispose(() => unmountComponent(instance), true)
 
   if (!isHydrating && _insertionParent) {
-    insert(instance.block, _insertionParent, _insertionAnchor)
+    mountComponent(instance, _insertionParent, _insertionAnchor)
   }
 
   return instance
diff --git a/packages/runtime-vapor/src/componentSlots.ts b/packages/runtime-vapor/src/componentSlots.ts
index 74296e09466..3ab49867e8d 100644
--- a/packages/runtime-vapor/src/componentSlots.ts
+++ b/packages/runtime-vapor/src/componentSlots.ts
@@ -1,11 +1,22 @@
-import { EMPTY_OBJ, NO, hasOwn, isArray, isFunction } from '@vue/shared'
+import {
+  EMPTY_OBJ,
+  NO,
+  SLOT_ANCHOR_LABEL,
+  hasOwn,
+  isArray,
+  isFunction,
+} from '@vue/shared'
 import { type Block, type BlockFn, DynamicFragment, insert } from './block'
 import { rawPropsProxyHandlers } from './componentProps'
 import { currentInstance, isRef } from '@vue/runtime-dom'
 import type { LooseRawProps, VaporComponentInstance } from './component'
 import { renderEffect } from './renderEffect'
-import { insertionAnchor, insertionParent } from './insertionState'
-import { isHydrating, locateHydrationNode } from './dom/hydration'
+import {
+  insertionAnchor,
+  insertionParent,
+  resetInsertionState,
+} from './insertionState'
+import { isHydrating } from './dom/hydration'
 
 export type RawSlots = Record<string, VaporSlot> & {
   $?: DynamicSlotSource[]
@@ -94,9 +105,7 @@ export function createSlot(
 ): Block {
   const _insertionParent = insertionParent
   const _insertionAnchor = insertionAnchor
-  if (isHydrating) {
-    locateHydrationNode()
-  }
+  if (!isHydrating) resetInsertionState()
 
   const instance = currentInstance as VaporComponentInstance
   const rawSlots = instance.rawSlots
@@ -105,7 +114,6 @@ export function createSlot(
     : EMPTY_OBJ
 
   let fragment: DynamicFragment
-
   if (isRef(rawSlots._)) {
     fragment = instance.appContext.vapor!.vdomSlot(
       rawSlots._,
@@ -115,7 +123,10 @@ export function createSlot(
       fallback,
     )
   } else {
-    fragment = __DEV__ ? new DynamicFragment('slot') : new DynamicFragment()
+    fragment =
+      isHydrating || __DEV__
+        ? new DynamicFragment(SLOT_ANCHOR_LABEL)
+        : new DynamicFragment()
     const isDynamicName = isFunction(name)
     const renderSlot = () => {
       const slot = getSlot(rawSlots, isFunction(name) ? name() : name)
@@ -145,7 +156,12 @@ export function createSlot(
     }
   }
 
-  if (!isHydrating && _insertionParent) {
+  if (
+    _insertionParent &&
+    (!isHydrating ||
+      // for vdom interop fragment, `fragment.insert` handles both hydration and mounting
+      fragment.insert)
+  ) {
     insert(fragment, _insertionParent, _insertionAnchor)
   }
 
diff --git a/packages/runtime-vapor/src/dom/hydration.ts b/packages/runtime-vapor/src/dom/hydration.ts
index d34d9db7da5..e3f666b5b26 100644
--- a/packages/runtime-vapor/src/dom/hydration.ts
+++ b/packages/runtime-vapor/src/dom/hydration.ts
@@ -5,7 +5,12 @@ import {
   resetInsertionState,
   setInsertionState,
 } from '../insertionState'
-import { child, next } from './node'
+import {
+  disableHydrationNodeLookup,
+  enableHydrationNodeLookup,
+  next,
+} from './node'
+import { isVaporAnchors, isVaporFragmentAnchor } from '@vue/shared'
 
 export let isHydrating = false
 export let currentHydrationNode: Node | null = null
@@ -16,25 +21,44 @@ export function setCurrentHydrationNode(node: Node | null): void {
 
 let isOptimized = false
 
-export function withHydration(container: ParentNode, fn: () => void): void {
-  adoptTemplate = adoptTemplateImpl
-  locateHydrationNode = locateHydrationNodeImpl
+function performHydration<T>(
+  fn: () => T,
+  setup: () => void,
+  cleanup: () => void,
+): T {
   if (!isOptimized) {
+    adoptTemplate = adoptTemplateImpl
+    locateHydrationNode = locateHydrationNodeImpl
+
     // optimize anchor cache lookup
     ;(Comment.prototype as any).$fs = undefined
     isOptimized = true
   }
+  enableHydrationNodeLookup()
   isHydrating = true
-  setInsertionState(container, 0)
+  setup()
   const res = fn()
-  resetInsertionState()
+  cleanup()
   currentHydrationNode = null
   isHydrating = false
+  disableHydrationNodeLookup()
   return res
 }
 
+export function withHydration(container: ParentNode, fn: () => void): void {
+  const setup = () => setInsertionState(container, 0)
+  const cleanup = () => resetInsertionState()
+  return performHydration(fn, setup, cleanup)
+}
+
+export function hydrateNode(node: Node, fn: () => void): void {
+  const setup = () => (currentHydrationNode = node)
+  const cleanup = () => {}
+  return performHydration(fn, setup, cleanup)
+}
+
 export let adoptTemplate: (node: Node, template: string) => Node | null
-export let locateHydrationNode: () => void
+export let locateHydrationNode: (hasFragmentAnchor?: boolean) => void
 
 type Anchor = Comment & {
   // cached matching fragment start to avoid repeated traversal
@@ -42,7 +66,7 @@ type Anchor = Comment & {
   $fs?: Anchor
 }
 
-const isComment = (node: Node, data: string): node is Anchor =>
+export const isComment = (node: Node, data: string): node is Anchor =>
   node.nodeType === 8 && (node as Comment).data === data
 
 /**
@@ -51,7 +75,7 @@ const isComment = (node: Node, data: string): node is Anchor =>
  */
 function adoptTemplateImpl(node: Node, template: string): Node | null {
   if (!(template[0] === '<' && template[1] === '!')) {
-    while (node.nodeType === 8) node = next(node)
+    while (node.nodeType === 8) node = node.nextSibling!
   }
 
   if (__DEV__) {
@@ -75,18 +99,31 @@ function adoptTemplateImpl(node: Node, template: string): Node | null {
   return node
 }
 
-function locateHydrationNodeImpl() {
-  let node: Node | null
+const hydrationPositionMap = new WeakMap<ParentNode, Node>()
 
+function locateHydrationNodeImpl(hasFragmentAnchor?: boolean) {
+  let node: Node | null
   // prepend / firstChild
   if (insertionAnchor === 0) {
-    node = child(insertionParent!)
-  } else {
+    node = insertionParent!.firstChild
+  } else if (insertionAnchor) {
+    // `insertionAnchor` is a Node, it is the DOM node to hydrate
+    // Template:   `...<span/><!----><span/>...`// `insertionAnchor` is the placeholder
+    // SSR Output: `...<span/>Content<span/>...`// `insertionAnchor` is the actual node
     node = insertionAnchor
-      ? insertionAnchor.previousSibling
-      : insertionParent
-        ? insertionParent.lastChild
-        : currentHydrationNode
+  } else {
+    node = insertionParent
+      ? hydrationPositionMap.get(insertionParent) || insertionParent.lastChild
+      : currentHydrationNode
+
+    // if node is a vapor fragment anchor, find the previous one
+    if (hasFragmentAnchor && node && isVaporFragmentAnchor(node)) {
+      node = node.previousSibling
+      if (__DEV__ && !node) {
+        // this should not happen
+        throw new Error(`vapor fragment anchor previous node was not found.`)
+      }
+    }
 
     if (node && isComment(node, ']')) {
       // fragment backward search
@@ -117,6 +154,11 @@ function locateHydrationNodeImpl() {
         }
       }
     }
+
+    if (insertionParent && node) {
+      const prev = node.previousSibling
+      if (prev) hydrationPositionMap.set(insertionParent, prev)
+    }
   }
 
   if (__DEV__ && !node) {
@@ -127,3 +169,51 @@ function locateHydrationNodeImpl() {
   resetInsertionState()
   currentHydrationNode = node
 }
+
+export function locateEndAnchor(
+  node: Node | null,
+  open = '[',
+  close = ']',
+): Node | null {
+  let match = 0
+  while (node) {
+    node = node.nextSibling
+    if (node && node.nodeType === 8) {
+      if ((node as Comment).data === open) match++
+      if ((node as Comment).data === close) {
+        if (match === 0) {
+          return node
+        } else {
+          match--
+        }
+      }
+    }
+  }
+  return null
+}
+
+export function isNonHydrationNode(node: Node): boolean {
+  return (
+    // empty text node
+    isEmptyTextNode(node) ||
+    // vdom fragment end anchor (`<!--]-->`)
+    isComment(node, ']') ||
+    // vapor mode specific anchors
+    isVaporAnchors(node)
+  )
+}
+
+export function locateVaporFragmentAnchor(
+  node: Node,
+  anchorLabel: string,
+): Comment | undefined {
+  let n = node.nextSibling
+  while (n) {
+    if (isComment(n, anchorLabel)) return n
+    n = n.nextSibling
+  }
+}
+
+export function isEmptyTextNode(node: Node): node is Text {
+  return node.nodeType === 3 && !(node as Text).data.trim()
+}
diff --git a/packages/runtime-vapor/src/dom/node.ts b/packages/runtime-vapor/src/dom/node.ts
index 83bc32c57f0..3f38c477a01 100644
--- a/packages/runtime-vapor/src/dom/node.ts
+++ b/packages/runtime-vapor/src/dom/node.ts
@@ -1,3 +1,10 @@
+import { isComment, isNonHydrationNode, locateEndAnchor } from './hydration'
+import {
+  DYNAMIC_END_ANCHOR_LABEL,
+  DYNAMIC_START_ANCHOR_LABEL,
+  isVaporAnchors,
+} from '@vue/shared'
+
 /*! #__NO_SIDE_EFFECTS__ */
 export function createTextNode(value = ''): Text {
   return document.createTextNode(value)
@@ -14,16 +21,175 @@ export function querySelector(selectors: string): Element | null {
 }
 
 /*! #__NO_SIDE_EFFECTS__ */
-export function child(node: ParentNode): Node {
+export function _child(node: ParentNode): Node {
   return node.firstChild!
 }
 
+/**
+ * Hydration-specific version of `child`.
+ *
+ * This function skips leading fragment anchors to find the first node relevant
+ * for hydration matching against the client-side template structure.
+ *
+ * Problem:
+ *   Template: `<div><slot />{{ msg }}</div>`
+ *
+ *   Client Compiled Code (Simplified):
+ *     const n2 = t0() // n2 = `<div> </div>`
+ *     const n1 = _child(n2) // n1 = text node
+ *     // ... slot creation ...
+ *     _renderEffect(() => _setText(n1, _ctx.msg))
+ *
+ *   SSR Output: `<div><!--[-->slot content<!--]-->Actual Text Node</div>`
+ *
+ *   Hydration Mismatch:
+ *   - During hydration, `n2` refers to the SSR `<div>`.
+ *   - `_child(n2)` would return `<!--[-->`.
+ *   - The client code expects `n1` to be the text node, but gets the comment.
+ *     The subsequent `_setText(n1, ...)` would fail or target the wrong node.
+ *
+ *   Solution (`__child`):
+ *   - `__child(n2)` is used during hydration. It skips the SSR fragment anchors
+ *     (`<!--[-->...<!--]-->`) and any other non-content nodes to find the
+ *     "Actual Text Node", correctly matching the client's expectation for `n1`.
+ */
+/*! #__NO_SIDE_EFFECTS__ */
+export function __child(node: ParentNode): Node {
+  let n = node.firstChild!
+
+  if (isComment(n, '[')) {
+    n = locateEndAnchor(n)!.nextSibling!
+  }
+
+  while (n && isVaporAnchors(n)) {
+    n = n.nextSibling!
+  }
+  return n
+}
+
 /*! #__NO_SIDE_EFFECTS__ */
-export function nthChild(node: Node, i: number): Node {
+export function _nthChild(node: Node, i: number): Node {
   return node.childNodes[i]
 }
 
+/**
+ * Hydration-specific version of `nthChild`.
+ */
 /*! #__NO_SIDE_EFFECTS__ */
-export function next(node: Node): Node {
+export function __nthChild(node: Node, i: number): Node {
+  let n = node.firstChild!
+  for (let start = 0; start < i; start++) {
+    n = __next(n) as ChildNode
+  }
+  return n
+}
+
+/*! #__NO_SIDE_EFFECTS__ */
+function _next(node: Node): Node {
   return node.nextSibling!
 }
+
+/**
+ * Hydration-specific version of `next`.
+ *
+ * SSR comment anchors (fragments `<!--[-->...<!--]-->`, dynamic `<!--[[-->...<!--]]-->`)
+ * disrupt standard `node.nextSibling` traversal during hydration. `_next` might
+ * return a comment node or an internal node of a fragment instead of skipping
+ * the entire fragment block.
+ *
+ * Example:
+ *   Template: `<div>Node1<!>Node2</div>` (where <!> is a dynamic component placeholder)
+ *
+ *   Client Compiled Code (Simplified):
+ *     const n2 = t0() // n2 = `<div>Node1<!---->Node2</div>`
+ *     const n1 = _next(_child(n2)) // n1 = _next(Node1) returns `<!---->`
+ *     _setInsertionState(n2, n1) // insertion anchor is `<!---->`
+ *     const n0 = _createComponent(_ctx.Comp) // inserted before `<!---->`
+ *
+ *   SSR Output: `<div>Node1<!--[-->Node3 Node4<!--]-->Node2</div>`
+ *
+ *   Hydration Mismatch:
+ *   - During hydration, `n2` refers to the SSR `<div>`.
+ *   - `_child(n2)` returns `Node1`.
+ *   - `_next(Node1)` would return `<!--[-->`.
+ *   - The client logic expects `n1` to be the node *after* `Node1` in its structure
+ *     (the placeholder), but gets the fragment start anchor `<!--[-->` from SSR.
+ *   - Using `<!--[-->` as the insertion anchor for hydrating the component is incorrect.
+ *
+ *   Solution (`__next`):
+ *   - During hydration, `next.impl` is `__next`.
+ *   - `n1 = __next(Node1)` is called.
+ *   - `__next` recognizes that the immediate sibling `<!--[-->` is a fragment start anchor.
+ *   - It skips the entire fragment block (`<!--[-->Node3 Node4<!--]-->`).
+ *   - It returns the node immediately *after* the fragment's end anchor, which is `Node2`.
+ *   - This correctly identifies the logical "next sibling" anchor (`Node2`) in the SSR structure,
+ *     allowing the component to be hydrated correctly relative to `Node1` and `Node2`.
+ *
+ * This function ensures traversal correctly skips over non-hydration nodes and
+ * treats entire fragment/dynamic blocks (when starting *from* their beginning anchor)
+ * as single logical units to find the next actual sibling node for hydration matching.
+ */
+/*! #__NO_SIDE_EFFECTS__ */
+export function __next(node: Node): Node {
+  // process dynamic node (<!--[[-->...<!--]]-->) as a single node
+  if (isComment(node, DYNAMIC_START_ANCHOR_LABEL)) {
+    node = locateEndAnchor(
+      node,
+      DYNAMIC_START_ANCHOR_LABEL,
+      DYNAMIC_END_ANCHOR_LABEL,
+    )!
+  }
+
+  // process fragment (<!--[-->...<!--]-->) as a single node
+  else if (isComment(node, '[')) {
+    node = locateEndAnchor(node)!
+  }
+
+  let n = node.nextSibling!
+  while (n && isNonHydrationNode(n)) {
+    n = n.nextSibling!
+  }
+  return n
+}
+
+type DelegatedFunction<T extends (...args: any[]) => any> = T & {
+  impl: T
+}
+
+/*! #__NO_SIDE_EFFECTS__ */
+export const child: DelegatedFunction<typeof _child> = node => {
+  return child.impl(node)
+}
+child.impl = _child
+
+/*! #__NO_SIDE_EFFECTS__ */
+export const next: DelegatedFunction<typeof _next> = node => {
+  return next.impl(node)
+}
+next.impl = _next
+
+/*! #__NO_SIDE_EFFECTS__ */
+export const nthChild: DelegatedFunction<typeof _nthChild> = (node, i) => {
+  return nthChild.impl(node, i)
+}
+nthChild.impl = _nthChild
+
+/**
+ * Enables hydration-specific node lookup behavior.
+ *
+ * Temporarily switches the implementations of the exported
+ * `child`, `next`, and `nthChild` functions to their hydration-specific
+ * versions (`__child`, `__next`, `__nthChild`). This allows traversal
+ * logic to correctly handle SSR comment anchors during hydration.
+ */
+export function enableHydrationNodeLookup(): void {
+  child.impl = __child
+  next.impl = __next
+  nthChild.impl = __nthChild
+}
+
+export function disableHydrationNodeLookup(): void {
+  child.impl = _child
+  next.impl = _next
+  nthChild.impl = _nthChild
+}
diff --git a/packages/runtime-vapor/src/vdomInterop.ts b/packages/runtime-vapor/src/vdomInterop.ts
index 77228fd72a0..4d1f4e6f184 100644
--- a/packages/runtime-vapor/src/vdomInterop.ts
+++ b/packages/runtime-vapor/src/vdomInterop.ts
@@ -2,6 +2,7 @@ import {
   type App,
   type ComponentInternalInstance,
   type ConcreteComponent,
+  type HydrationRenderer,
   MoveType,
   type Plugin,
   type RendererInternals,
@@ -11,6 +12,7 @@ import {
   type VaporInteropInterface,
   createVNode,
   currentInstance,
+  ensureHydrationRenderer,
   ensureRenderer,
   onScopeDispose,
   renderSlot,
@@ -33,6 +35,12 @@ import type { RawSlots, VaporSlot } from './componentSlots'
 import { renderEffect } from './renderEffect'
 import { createTextNode } from './dom/node'
 import { optimizePropertyLookup } from './dom/prop'
+import {
+  currentHydrationNode,
+  isHydrating,
+  locateHydrationNode,
+  hydrateNode as vaporHydrateNode,
+} from './dom/hydration'
 
 // mounting vapor components and slots in vdom
 const vaporInteropImpl: Omit<
@@ -113,6 +121,8 @@ const vaporInteropImpl: Omit<
     insert(vnode.vb || (vnode.component as any), container, anchor)
     insert(vnode.anchor as any, container, anchor)
   },
+
+  hydrate: vaporHydrateNode,
 }
 
 const vaporSlotPropsProxyHandler: ProxyHandler<
@@ -139,6 +149,8 @@ const vaporSlotsProxyHandler: ProxyHandler<any> = {
   },
 }
 
+let vdomHydrateNode: HydrationRenderer['hydrateNode'] | undefined
+
 /**
  * Mount vdom component in vapor
  */
@@ -176,16 +188,30 @@ function createVDOMComponent(
   }
 
   frag.insert = (parentNode, anchor) => {
-    if (!isMounted) {
-      internals.mt(
-        vnode,
-        parentNode,
-        anchor,
-        parentInstance as any,
-        null,
-        undefined,
-        false,
-      )
+    if (!isMounted || isHydrating) {
+      if (isHydrating) {
+        ;(
+          vdomHydrateNode ||
+          (vdomHydrateNode = ensureHydrationRenderer().hydrateNode!)
+        )(
+          currentHydrationNode!,
+          vnode,
+          parentInstance as any,
+          null,
+          null,
+          false,
+        )
+      } else {
+        internals.mt(
+          vnode,
+          parentNode,
+          anchor,
+          parentInstance as any,
+          null,
+          undefined,
+          false,
+        )
+      }
       onScopeDispose(unmount, true)
       isMounted = true
     } else {
@@ -230,28 +256,43 @@ function renderVDOMSlot(
           isFunction(name) ? name() : name,
           props,
         )
-        if ((vnode.children as any[]).length) {
-          if (fallbackNodes) {
-            remove(fallbackNodes, parentNode)
-            fallbackNodes = undefined
-          }
-          internals.p(
-            oldVNode,
+        if (isHydrating) {
+          locateHydrationNode(true)
+          ;(
+            vdomHydrateNode ||
+            (vdomHydrateNode = ensureHydrationRenderer().hydrateNode!)
+          )(
+            currentHydrationNode!,
             vnode,
-            parentNode,
-            anchor,
             parentComponent as any,
+            null,
+            null,
+            false,
           )
-          oldVNode = vnode
         } else {
-          if (fallback && !fallbackNodes) {
-            // mount fallback
-            if (oldVNode) {
-              internals.um(oldVNode, parentComponent as any, null, true)
+          if ((vnode.children as any[]).length) {
+            if (fallbackNodes) {
+              remove(fallbackNodes, parentNode)
+              fallbackNodes = undefined
+            }
+            internals.p(
+              oldVNode,
+              vnode,
+              parentNode,
+              anchor,
+              parentComponent as any,
+            )
+            oldVNode = vnode
+          } else {
+            if (fallback && !fallbackNodes) {
+              // mount fallback
+              if (oldVNode) {
+                internals.um(oldVNode, parentComponent as any, null, true)
+              }
+              insert((fallbackNodes = fallback(props)), parentNode, anchor)
             }
-            insert((fallbackNodes = fallback(props)), parentNode, anchor)
+            oldVNode = null
           }
-          oldVNode = null
         }
       })
       isMounted = true
diff --git a/packages/server-renderer/__tests__/render.spec.ts b/packages/server-renderer/__tests__/render.spec.ts
index d0a5223b2ff..03b7402ff92 100644
--- a/packages/server-renderer/__tests__/render.spec.ts
+++ b/packages/server-renderer/__tests__/render.spec.ts
@@ -446,7 +446,7 @@ function testRender(type: string, render: typeof renderToString) {
         ).toBe(
           `<div>parent<div class="child">` +
             `<!--[--><span>from slot</span><!--]-->` +
-            `</div></div>`,
+            `<!--slot--></div></div>`,
         )
 
         // test fallback
@@ -461,7 +461,7 @@ function testRender(type: string, render: typeof renderToString) {
             }),
           ),
         ).toBe(
-          `<div>parent<div class="child"><!--[-->fallback<!--]--></div></div>`,
+          `<div>parent<div class="child"><!--[-->fallback<!--]--><!--slot--></div></div>`,
         )
       })
 
@@ -507,7 +507,7 @@ function testRender(type: string, render: typeof renderToString) {
         ).toBe(
           `<div>parent<div class="child">` +
             `<!--[--><span>from slot</span><!--]-->` +
-            `</div></div>`,
+            `<!--slot--></div></div>`,
         )
       })
 
@@ -525,7 +525,7 @@ function testRender(type: string, render: typeof renderToString) {
         expect(await render(app)).toBe(
           `<div>parent<div class="child">` +
             `<!--[--><span>from slot</span><!--]-->` +
-            `</div></div>`,
+            `<!--slot--></div></div>`,
         )
       })
 
@@ -572,7 +572,7 @@ function testRender(type: string, render: typeof renderToString) {
         })
 
         expect(await render(app)).toBe(
-          `<div><!--[--><!--[-->hello<!--]--><!--]--></div>`,
+          `<div><!--[--><!--[-->hello<!--]--><!--slot--><!--]--><!--slot--></div>`,
         )
       })
 
@@ -593,7 +593,7 @@ function testRender(type: string, render: typeof renderToString) {
 
         expect(await render(app)).toBe(
           // should only have a single fragment
-          `<div><!--[--><!--]--></div>`,
+          `<div><!--[--><!--]--><!--slot--></div>`,
         )
       })
 
@@ -614,7 +614,7 @@ function testRender(type: string, render: typeof renderToString) {
 
         expect(await render(app)).toBe(
           // should only have a single fragment
-          `<div><!--[-->fallback<!--]--></div>`,
+          `<div><!--[-->fallback<!--]--><!--slot--></div>`,
         )
       })
     })
diff --git a/packages/server-renderer/__tests__/ssrAttrFallthrough.spec.ts b/packages/server-renderer/__tests__/ssrAttrFallthrough.spec.ts
index e8cfa75e77c..cbf13db254e 100644
--- a/packages/server-renderer/__tests__/ssrAttrFallthrough.spec.ts
+++ b/packages/server-renderer/__tests__/ssrAttrFallthrough.spec.ts
@@ -25,7 +25,7 @@ describe('ssr: attr fallthrough', () => {
       template: `<child :ok="ok" class="bar"/>`,
     }
     expect(await renderToString(createApp(Parent, { ok: true }))).toBe(
-      `<div class="foo bar"></div>`,
+      `<div class="foo bar"></div><!--if-->`,
     )
     expect(await renderToString(createApp(Parent, { ok: false }))).toBe(
       `<span class="bar"></span>`,
diff --git a/packages/server-renderer/__tests__/ssrDynamicComponent.spec.ts b/packages/server-renderer/__tests__/ssrDynamicComponent.spec.ts
index 0679c82168b..181720c5b36 100644
--- a/packages/server-renderer/__tests__/ssrDynamicComponent.spec.ts
+++ b/packages/server-renderer/__tests__/ssrDynamicComponent.spec.ts
@@ -14,7 +14,9 @@ describe('ssr: dynamic component', () => {
           template: `<component :is="'one'"><span>slot</span></component>`,
         }),
       ),
-    ).toBe(`<div><!--[--><span>slot</span><!--]--></div>`)
+    ).toBe(
+      `<div><!--[--><span>slot</span><!--]--><!--slot--></div><!--dynamic-component-->`,
+    )
   })
 
   test('resolved to component with v-show', async () => {
@@ -30,7 +32,7 @@ describe('ssr: dynamic component', () => {
         }),
       ),
     ).toBe(
-      `<div><!--[--><div style=\"display:none;\"><!--[-->hi<!--]--></div><!--]--></div>`,
+      `<div><!--[--><div style="display:none;"><!--[-->hi<!--]--></div><!--dynamic-component--><!--]--></div><!--dynamic-component-->`,
     )
   })
 
@@ -41,7 +43,7 @@ describe('ssr: dynamic component', () => {
           template: `<component :is="'p'"><span>slot</span></component>`,
         }),
       ),
-    ).toBe(`<p><span>slot</span></p>`)
+    ).toBe(`<p><span>slot</span></p><!--dynamic-component-->`)
   })
 
   test('resolve to component vnode', async () => {
@@ -60,7 +62,9 @@ describe('ssr: dynamic component', () => {
           template: `<component :is="vnode"><span>slot</span></component>`,
         }),
       ),
-    ).toBe(`<div>test<!--[--><span>slot</span><!--]--></div>`)
+    ).toBe(
+      `<div>test<!--[--><span>slot</span><!--]--><!--slot--></div><!--dynamic-component-->`,
+    )
   })
 
   test('resolve to element vnode', async () => {
@@ -75,6 +79,6 @@ describe('ssr: dynamic component', () => {
           template: `<component :is="vnode"><span>slot</span></component>`,
         }),
       ),
-    ).toBe(`<div id="test"><span>slot</span></div>`)
+    ).toBe(`<div id="test"><span>slot</span></div><!--dynamic-component-->`)
   })
 })
diff --git a/packages/server-renderer/__tests__/ssrScopeId.spec.ts b/packages/server-renderer/__tests__/ssrScopeId.spec.ts
index 4ceb865fb50..c4135e498b7 100644
--- a/packages/server-renderer/__tests__/ssrScopeId.spec.ts
+++ b/packages/server-renderer/__tests__/ssrScopeId.spec.ts
@@ -68,7 +68,9 @@ describe('ssr: scopedId runtime behavior', () => {
     }
 
     const result = await renderToString(createApp(Comp))
-    expect(result).toBe(`<!--[--><div parent wrapper-s child></div><!--]-->`)
+    expect(result).toBe(
+      `<!--[--><div parent wrapper-s child></div><!--]--><!--slot-->`,
+    )
   })
 
   // #2892
@@ -150,8 +152,8 @@ describe('ssr: scopedId runtime behavior', () => {
     const result = await renderToString(createApp(Root))
     expect(result).toBe(
       `<div class="wrapper" root slotted wrapper>` +
-        `<!--[--><!--[--><div root slotted-s wrapper-s></div><!--]--><!--]-->` +
-        `</div>`,
+        `<!--[--><!--[--><div root slotted-s wrapper-s></div><!--]--><!--slot--><!--]-->` +
+        `<!--slot--></div>`,
     )
   })
 
@@ -265,8 +267,8 @@ describe('ssr: scopedId runtime behavior', () => {
     const result = await renderToString(createApp(Root))
     expect(result).toBe(
       `<div class="wrapper" root slotted wrapper>` +
-        `<!--[--><!--[--><div root slotted-s wrapper-s></div><!--]--><!--]-->` +
-        `</div>`,
+        `<!--[--><!--[--><div root slotted-s wrapper-s></div><!--]--><!--slot--><!--]-->` +
+        `<!--slot--></div>`,
     )
   })
 })
diff --git a/packages/server-renderer/__tests__/ssrSlot.spec.ts b/packages/server-renderer/__tests__/ssrSlot.spec.ts
index 02872274ab6..d17e34bc7c0 100644
--- a/packages/server-renderer/__tests__/ssrSlot.spec.ts
+++ b/packages/server-renderer/__tests__/ssrSlot.spec.ts
@@ -16,7 +16,7 @@ describe('ssr: slot', () => {
           template: `<one>hello</one>`,
         }),
       ),
-    ).toBe(`<div><!--[-->hello<!--]--></div>`)
+    ).toBe(`<div><!--[-->hello<!--]--><!--slot--></div>`)
   })
 
   test('element slot', async () => {
@@ -27,7 +27,7 @@ describe('ssr: slot', () => {
           template: `<one><div>hi</div></one>`,
         }),
       ),
-    ).toBe(`<div><!--[--><div>hi</div><!--]--></div>`)
+    ).toBe(`<div><!--[--><div>hi</div><!--]--><!--slot--></div>`)
   })
 
   test('empty slot', async () => {
@@ -42,7 +42,7 @@ describe('ssr: slot', () => {
           template: `<one><template v-if="false"/></one>`,
         }),
       ),
-    ).toBe(`<div><!--[--><!--]--></div>`)
+    ).toBe(`<div><!--[--><!--]--><!--slot--></div>`)
   })
 
   test('empty slot (manual comments)', async () => {
@@ -57,7 +57,7 @@ describe('ssr: slot', () => {
           template: `<one><!--hello--></one>`,
         }),
       ),
-    ).toBe(`<div><!--[--><!--]--></div>`)
+    ).toBe(`<div><!--[--><!--]--><!--slot--></div>`)
   })
 
   test('empty slot (multi-line comments)', async () => {
@@ -72,7 +72,7 @@ describe('ssr: slot', () => {
           template: `<one><!--he\nllo--></one>`,
         }),
       ),
-    ).toBe(`<div><!--[--><!--]--></div>`)
+    ).toBe(`<div><!--[--><!--]--><!--slot--></div>`)
   })
 
   test('multiple elements', async () => {
@@ -83,7 +83,7 @@ describe('ssr: slot', () => {
           template: `<one><div>one</div><div>two</div></one>`,
         }),
       ),
-    ).toBe(`<div><!--[--><div>one</div><div>two</div><!--]--></div>`)
+    ).toBe(`<div><!--[--><div>one</div><div>two</div><!--]--><!--slot--></div>`)
   })
 
   test('fragment slot (template v-if)', async () => {
@@ -94,7 +94,9 @@ describe('ssr: slot', () => {
           template: `<one><template v-if="true">hello</template></one>`,
         }),
       ),
-    ).toBe(`<div><!--[--><!--[-->hello<!--]--><!--]--></div>`)
+    ).toBe(
+      `<div><!--[--><!--[-->hello<!--]--><!--if--><!--]--><!--slot--></div>`,
+    )
   })
 
   test('fragment slot (template v-if + multiple elements)', async () => {
@@ -106,7 +108,7 @@ describe('ssr: slot', () => {
         }),
       ),
     ).toBe(
-      `<div><!--[--><!--[--><div>one</div><div>two</div><!--]--><!--]--></div>`,
+      `<div><!--[--><!--[--><div>one</div><div>two</div><!--]--><!--if--><!--]--><!--slot--></div>`,
     )
   })
 
@@ -135,7 +137,7 @@ describe('ssr: slot', () => {
           template: `<one><div v-if="true">foo</div></one>`,
         }),
       ),
-    ).toBe(`<div>foo</div>`)
+    ).toBe(`<div>foo</div><!--if-->`)
   })
 
   // #9933
@@ -170,7 +172,9 @@ describe('ssr: slot', () => {
           template: `<ButtonComp><Wrap><div v-if="false">hello</div></Wrap></ButtonComp>`,
         }),
       ),
-    ).toBe(`<button><!--[--><div><!--[--><!--]--></div><!--]--></button>`)
+    ).toBe(
+      `<button><!--[--><div><!--[--><!--]--><!--slot--></div><!--]--></button><!--dynamic-component-->`,
+    )
 
     expect(
       await renderToString(
@@ -187,7 +191,7 @@ describe('ssr: slot', () => {
         }),
       ),
     ).toBe(
-      `<button><!--[--><div><!--[--><div>hello</div><!--]--></div><!--]--></button>`,
+      `<button><!--[--><div><!--[--><div>hello</div><!--]--><!--slot--></div><!--]--></button><!--dynamic-component-->`,
     )
 
     expect(
@@ -201,6 +205,6 @@ describe('ssr: slot', () => {
           template: `<ButtonComp><template v-if="false">hello</template></ButtonComp>`,
         }),
       ),
-    ).toBe(`<button><!--[--><!--]--></button>`)
+    ).toBe(`<button><!--[--><!--]--></button><!--dynamic-component-->`)
   })
 })
diff --git a/packages/server-renderer/src/helpers/ssrRenderSlot.ts b/packages/server-renderer/src/helpers/ssrRenderSlot.ts
index 19aa4ce63b7..0733c823390 100644
--- a/packages/server-renderer/src/helpers/ssrRenderSlot.ts
+++ b/packages/server-renderer/src/helpers/ssrRenderSlot.ts
@@ -5,7 +5,7 @@ import {
   type SSRBufferItem,
   renderVNodeChildren,
 } from '../render'
-import { isArray } from '@vue/shared'
+import { SLOT_ANCHOR_LABEL, isArray } from '@vue/shared'
 
 const { ensureValidVNode } = ssrUtils
 
@@ -37,7 +37,7 @@ export function ssrRenderSlot(
     parentComponent,
     slotScopeId,
   )
-  push(`<!--]-->`)
+  push(`<!--]--><!--${SLOT_ANCHOR_LABEL}-->`)
 }
 
 export function ssrRenderSlotInner(
@@ -104,7 +104,7 @@ export function ssrRenderSlotInner(
         if (
           transition &&
           slotBuffer[0] === '<!--[-->' &&
-          slotBuffer[end - 1] === '<!--]-->'
+          (slotBuffer[end - 1] as string).startsWith('<!--]-->')
         ) {
           start++
           end--
diff --git a/packages/shared/src/domAnchors.ts b/packages/shared/src/domAnchors.ts
new file mode 100644
index 00000000000..f807ee169db
--- /dev/null
+++ b/packages/shared/src/domAnchors.ts
@@ -0,0 +1,32 @@
+export const DYNAMIC_START_ANCHOR_LABEL = '[['
+export const DYNAMIC_END_ANCHOR_LABEL = ']]'
+
+export const IF_ANCHOR_LABEL: string = 'if'
+export const DYNAMIC_COMPONENT_ANCHOR_LABEL: string = 'dynamic-component'
+export const FOR_ANCHOR_LABEL: string = 'for'
+export const SLOT_ANCHOR_LABEL: string = 'slot'
+
+export function isDynamicAnchor(node: Node): node is Comment {
+  if (node.nodeType !== 8) return false
+
+  const data = (node as Comment).data
+  return (
+    data === DYNAMIC_START_ANCHOR_LABEL || data === DYNAMIC_END_ANCHOR_LABEL
+  )
+}
+
+export function isVaporFragmentAnchor(node: Node): node is Comment {
+  if (node.nodeType !== 8) return false
+
+  const data = (node as Comment).data
+  return (
+    data === IF_ANCHOR_LABEL ||
+    data === FOR_ANCHOR_LABEL ||
+    data === SLOT_ANCHOR_LABEL ||
+    data === DYNAMIC_COMPONENT_ANCHOR_LABEL
+  )
+}
+
+export function isVaporAnchors(node: Node): node is Comment {
+  return isDynamicAnchor(node) || isVaporFragmentAnchor(node)
+}
diff --git a/packages/shared/src/index.ts b/packages/shared/src/index.ts
index dc56faf8cf5..674bcdf96cd 100644
--- a/packages/shared/src/index.ts
+++ b/packages/shared/src/index.ts
@@ -13,3 +13,4 @@ export * from './looseEqual'
 export * from './toDisplayString'
 export * from './typeUtils'
 export * from './subSequence'
+export * from './domAnchors'
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index bc4e5a164ba..c72eaa1ab19 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -13,8 +13,8 @@ catalogs:
       specifier: ^7.26.10
       version: 7.26.10
     '@vitejs/plugin-vue':
-      specifier: ^5.2.3
-      version: 5.2.3
+      specifier: https://pkg.pr.new/@vitejs/plugin-vue@c156992
+      version: 5.2.1
     estree-walker:
       specifier: ^2.0.2
       version: 2.0.2
@@ -25,8 +25,8 @@ catalogs:
       specifier: ^1.2.1
       version: 1.2.1
     vite:
-      specifier: ^5.4.14
-      version: 5.4.14
+      specifier: ^6.1.0
+      version: 6.2.4
 
 importers:
 
@@ -69,14 +69,14 @@ importers:
         specifier: ^6.1.4
         version: 6.1.4
       '@vitest/coverage-v8':
-        specifier: ^3.0.2
-        version: 3.0.2(vitest@3.0.2)
+        specifier: ^3.0.9
+        version: 3.0.9(vitest@3.0.9)
       '@vitest/eslint-plugin':
-        specifier: ^1.1.25
-        version: 1.1.25(@typescript-eslint/utils@8.20.0(eslint@9.18.0)(typescript@5.6.2))(eslint@9.18.0)(typescript@5.6.2)(vitest@3.0.2)
+        specifier: ^1.1.38
+        version: 1.1.38(@typescript-eslint/utils@8.27.0(eslint@9.23.0)(typescript@5.6.2))(eslint@9.23.0)(typescript@5.6.2)(vitest@3.0.9)
       '@vitest/ui':
         specifier: ^3.0.2
-        version: 3.0.4(vitest@3.0.2)
+        version: 3.1.1(vitest@3.0.9)
       '@vue/consolidate':
         specifier: 1.0.0
         version: 1.0.0
@@ -178,16 +178,16 @@ importers:
         version: 8.27.0(eslint@9.23.0)(typescript@5.6.2)
       vite:
         specifier: 'catalog:'
-        version: 6.1.0(@types/node@22.10.7)(sass@1.83.4)(terser@5.33.0)(yaml@2.6.1)
+        version: 6.2.4(@types/node@22.13.13)(sass@1.86.0)(terser@5.33.0)(yaml@2.7.0)
       vitest:
-        specifier: ^3.0.2
-        version: 3.0.2(@types/node@22.10.7)(@vitest/ui@3.0.4)(jsdom@26.0.0)(sass@1.83.4)(terser@5.33.0)(yaml@2.6.1)
+        specifier: ^3.0.9
+        version: 3.0.9(@types/node@22.13.13)(@vitest/ui@3.1.1)(jsdom@26.0.0)(sass@1.86.0)(terser@5.33.0)(yaml@2.7.0)
 
   packages-private/benchmark:
     dependencies:
       '@vitejs/plugin-vue':
         specifier: 'catalog:'
-        version: https://pkg.pr.new/@vitejs/plugin-vue@c156992(vite@6.1.0(@types/node@22.10.7)(sass@1.83.4)(terser@5.33.0)(yaml@2.6.1))(vue@3.5.13(typescript@5.6.2))
+        version: https://pkg.pr.new/@vitejs/plugin-vue@c156992(vite@6.2.4(@types/node@22.13.13)(sass@1.86.0)(terser@5.33.0)(yaml@2.7.0))(vue@3.5.13(typescript@5.6.2))
       connect:
         specifier: ^3.7.0
         version: 3.7.0
@@ -196,7 +196,7 @@ importers:
         version: 2.0.4
       vite:
         specifier: 'catalog:'
-        version: 6.1.0(@types/node@22.10.7)(sass@1.83.4)(terser@5.33.0)(yaml@2.6.1)
+        version: 6.2.4(@types/node@22.13.13)(sass@1.86.0)(terser@5.33.0)(yaml@2.7.0)
     devDependencies:
       '@types/connect':
         specifier: ^3.4.38
@@ -227,26 +227,26 @@ importers:
     dependencies:
       '@vueuse/core':
         specifier: ^11.1.0
-        version: 11.1.0(vue@packages+vue)
+        version: 11.3.0(vue@packages+vue)
       vue:
         specifier: workspace:*
         version: link:../../packages/vue
     devDependencies:
       '@vitejs/plugin-vue':
         specifier: 'catalog:'
-        version: https://pkg.pr.new/@vitejs/plugin-vue@c156992(vite@6.1.0(@types/node@22.10.7)(sass@1.83.4)(terser@5.33.0)(yaml@2.6.1))(vue@packages+vue)
+        version: https://pkg.pr.new/@vitejs/plugin-vue@c156992(vite@6.2.4(@types/node@22.13.13)(sass@1.86.0)(terser@5.33.0)(yaml@2.7.0))(vue@packages+vue)
       '@vue/compiler-sfc':
         specifier: workspace:*
         version: link:../../packages/compiler-sfc
       vite:
         specifier: 'catalog:'
-        version: 6.1.0(@types/node@22.10.7)(sass@1.83.4)(terser@5.33.0)(yaml@2.6.1)
+        version: 6.2.4(@types/node@22.13.13)(sass@1.86.0)(terser@5.33.0)(yaml@2.7.0)
       vite-hyper-config:
         specifier: ^0.4.0
-        version: 0.4.0(@types/node@22.10.7)(sass@1.83.4)(terser@5.33.0)(vite@6.1.0(@types/node@22.10.7)(sass@1.83.4)(terser@5.33.0)(yaml@2.6.1))
+        version: 0.4.1(@types/node@22.13.13)(sass@1.86.0)(terser@5.33.0)(vite@6.2.4(@types/node@22.13.13)(sass@1.86.0)(terser@5.33.0)(yaml@2.7.0))
       vite-plugin-inspect:
         specifier: ^0.8.7
-        version: 0.8.7(rollup@4.31.0)(vite@6.1.0(@types/node@22.10.7)(sass@1.83.4)(terser@5.33.0)(yaml@2.6.1))
+        version: 0.8.7(rollup@4.37.0)(vite@6.2.4(@types/node@22.13.13)(sass@1.86.0)(terser@5.33.0)(yaml@2.7.0))
 
   packages-private/sfc-playground:
     dependencies:
@@ -265,10 +265,10 @@ importers:
     devDependencies:
       '@vitejs/plugin-vue':
         specifier: 'catalog:'
-        version: https://pkg.pr.new/@vitejs/plugin-vue@c156992(vite@6.1.0(@types/node@22.10.7)(sass@1.83.4)(terser@5.33.0)(yaml@2.6.1))(vue@packages+vue)
+        version: https://pkg.pr.new/@vitejs/plugin-vue@c156992(vite@6.2.4(@types/node@22.13.13)(sass@1.86.0)(terser@5.33.0)(yaml@2.7.0))(vue@packages+vue)
       vite:
         specifier: 'catalog:'
-        version: 6.1.0(@types/node@22.10.7)(sass@1.83.4)(terser@5.33.0)(yaml@2.6.1)
+        version: 6.2.4(@types/node@22.13.13)(sass@1.86.0)(terser@5.33.0)(yaml@2.7.0)
 
   packages-private/template-explorer:
     dependencies:
@@ -289,7 +289,7 @@ importers:
         version: 3.4.38
       '@vitejs/plugin-vue':
         specifier: 'catalog:'
-        version: https://pkg.pr.new/@vitejs/plugin-vue@c156992(vite@6.1.0(@types/node@22.10.7)(sass@1.83.4)(terser@5.33.0)(yaml@2.6.1))(vue@packages+vue)
+        version: https://pkg.pr.new/@vitejs/plugin-vue@c156992(vite@6.2.4(@types/node@22.13.13)(sass@1.86.0)(terser@5.33.0)(yaml@2.7.0))(vue@packages+vue)
       connect:
         specifier: ^3.7.0
         version: 3.7.0
@@ -298,7 +298,7 @@ importers:
         version: 2.0.4
       vite:
         specifier: 'catalog:'
-        version: 6.1.0(@types/node@22.10.7)(sass@1.83.4)(terser@5.33.0)(yaml@2.6.1)
+        version: 6.2.4(@types/node@22.13.13)(sass@1.86.0)(terser@5.33.0)(yaml@2.7.0)
       vue:
         specifier: workspace:*
         version: link:../../packages/vue
@@ -307,10 +307,10 @@ importers:
     devDependencies:
       '@vitejs/plugin-vue':
         specifier: 'catalog:'
-        version: 5.2.3(vite@5.4.14(@types/node@22.13.13)(sass@1.86.0))(vue@packages+vue)
+        version: https://pkg.pr.new/@vitejs/plugin-vue@c156992(vite@6.2.4(@types/node@22.13.13)(sass@1.86.0)(terser@5.33.0)(yaml@2.7.0))(vue@packages+vue)
       vite:
         specifier: 'catalog:'
-        version: 5.4.14(@types/node@22.13.13)(sass@1.86.0)
+        version: 6.2.4(@types/node@22.13.13)(sass@1.86.0)(terser@5.33.0)(yaml@2.7.0)
       vue:
         specifier: workspace:*
         version: link:../../packages/vue
@@ -1115,6 +1115,9 @@ packages:
     resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==}
     engines: {node: '>=14'}
 
+  '@polka/url@1.0.0-next.28':
+    resolution: {integrity: sha512-8LduaNlMZGwdZ6qWrKlfa+2M4gahzFkprZiAt2TF8uS0qQgBizKXpXURqvTJ4WtmupWxaLqjRb2UCTe72mu+Aw==}
+
   '@puppeteer/browsers@2.8.0':
     resolution: {integrity: sha512-yTwt2KWRmCQAfhvbCRjebaSX8pV1//I0Y3g+A7f/eS7gf0l4eRJoUCvcYdVtboeU4CTOZQuqYbZNS8aBYb8ROQ==}
     engines: {node: '>=18'}
@@ -1183,100 +1186,53 @@ packages:
       rollup:
         optional: true
 
-  '@rollup/rollup-android-arm-eabi@4.34.2':
-    resolution: {integrity: sha512-6Fyg9yQbwJR+ykVdT9sid1oc2ewejS6h4wzQltmJfSW53N60G/ah9pngXGANdy9/aaE/TcUFpWosdm7JXS1WTQ==}
-    cpu: [arm]
-    os: [android]
-
   '@rollup/rollup-android-arm-eabi@4.37.0':
     resolution: {integrity: sha512-l7StVw6WAa8l3vA1ov80jyetOAEo1FtHvZDbzXDO/02Sq/QVvqlHkYoFwDJPIMj0GKiistsBudfx5tGFnwYWDQ==}
     cpu: [arm]
     os: [android]
 
-  '@rollup/rollup-android-arm64@4.34.2':
-    resolution: {integrity: sha512-K5GfWe+vtQ3kyEbihrimM38UgX57UqHp+oME7X/EX9Im6suwZfa7Hsr8AtzbJvukTpwMGs+4s29YMSO3rwWtsw==}
-    cpu: [arm64]
-    os: [android]
-
   '@rollup/rollup-android-arm64@4.37.0':
     resolution: {integrity: sha512-6U3SlVyMxezt8Y+/iEBcbp945uZjJwjZimu76xoG7tO1av9VO691z8PkhzQ85ith2I8R2RddEPeSfcbyPfD4hA==}
     cpu: [arm64]
     os: [android]
 
-  '@rollup/rollup-darwin-arm64@4.34.2':
-    resolution: {integrity: sha512-PSN58XG/V/tzqDb9kDGutUruycgylMlUE59f40ny6QIRNsTEIZsrNQTJKUN2keMMSmlzgunMFqyaGLmly39sug==}
-    cpu: [arm64]
-    os: [darwin]
-
   '@rollup/rollup-darwin-arm64@4.37.0':
     resolution: {integrity: sha512-+iTQ5YHuGmPt10NTzEyMPbayiNTcOZDWsbxZYR1ZnmLnZxG17ivrPSWFO9j6GalY0+gV3Jtwrrs12DBscxnlYA==}
     cpu: [arm64]
     os: [darwin]
 
-  '@rollup/rollup-darwin-x64@4.34.2':
-    resolution: {integrity: sha512-gQhK788rQJm9pzmXyfBB84VHViDERhAhzGafw+E5mUpnGKuxZGkMVDa3wgDFKT6ukLC5V7QTifzsUKdNVxp5qQ==}
-    cpu: [x64]
-    os: [darwin]
-
   '@rollup/rollup-darwin-x64@4.37.0':
     resolution: {integrity: sha512-m8W2UbxLDcmRKVjgl5J/k4B8d7qX2EcJve3Sut7YGrQoPtCIQGPH5AMzuFvYRWZi0FVS0zEY4c8uttPfX6bwYQ==}
     cpu: [x64]
     os: [darwin]
 
-  '@rollup/rollup-freebsd-arm64@4.34.2':
-    resolution: {integrity: sha512-eiaHgQwGPpxLC3+zTAcdKl4VsBl3r0AiJOd1Um/ArEzAjN/dbPK1nROHrVkdnoE6p7Svvn04w3f/jEZSTVHunA==}
-    cpu: [arm64]
-    os: [freebsd]
-
   '@rollup/rollup-freebsd-arm64@4.37.0':
     resolution: {integrity: sha512-FOMXGmH15OmtQWEt174v9P1JqqhlgYge/bUjIbiVD1nI1NeJ30HYT9SJlZMqdo1uQFyt9cz748F1BHghWaDnVA==}
     cpu: [arm64]
     os: [freebsd]
 
-  '@rollup/rollup-freebsd-x64@4.34.2':
-    resolution: {integrity: sha512-lhdiwQ+jf8pewYOTG4bag0Qd68Jn1v2gO1i0mTuiD+Qkt5vNfHVK/jrT7uVvycV8ZchlzXp5HDVmhpzjC6mh0g==}
-    cpu: [x64]
-    os: [freebsd]
-
   '@rollup/rollup-freebsd-x64@4.37.0':
     resolution: {integrity: sha512-SZMxNttjPKvV14Hjck5t70xS3l63sbVwl98g3FlVVx2YIDmfUIy29jQrsw06ewEYQ8lQSuY9mpAPlmgRD2iSsA==}
     cpu: [x64]
     os: [freebsd]
 
-  '@rollup/rollup-linux-arm-gnueabihf@4.34.2':
-    resolution: {integrity: sha512-lfqTpWjSvbgQP1vqGTXdv+/kxIznKXZlI109WkIFPbud41bjigjNmOAAKoazmRGx+k9e3rtIdbq2pQZPV1pMig==}
-    cpu: [arm]
-    os: [linux]
-
   '@rollup/rollup-linux-arm-gnueabihf@4.37.0':
     resolution: {integrity: sha512-hhAALKJPidCwZcj+g+iN+38SIOkhK2a9bqtJR+EtyxrKKSt1ynCBeqrQy31z0oWU6thRZzdx53hVgEbRkuI19w==}
     cpu: [arm]
     os: [linux]
-
-  '@rollup/rollup-linux-arm-musleabihf@4.34.2':
-    resolution: {integrity: sha512-RGjqULqIurqqv+NJTyuPgdZhka8ImMLB32YwUle2BPTDqDoXNgwFjdjQC59FbSk08z0IqlRJjrJ0AvDQ5W5lpw==}
-    cpu: [arm]
-    os: [linux]
+    libc: [glibc]
 
   '@rollup/rollup-linux-arm-musleabihf@4.37.0':
     resolution: {integrity: sha512-jUb/kmn/Gd8epbHKEqkRAxq5c2EwRt0DqhSGWjPFxLeFvldFdHQs/n8lQ9x85oAeVb6bHcS8irhTJX2FCOd8Ag==}
     cpu: [arm]
     os: [linux]
-
-  '@rollup/rollup-linux-arm64-gnu@4.34.2':
-    resolution: {integrity: sha512-ZvkPiheyXtXlFqHpsdgscx+tZ7hoR59vOettvArinEspq5fxSDSgfF+L5wqqJ9R4t+n53nyn0sKxeXlik7AY9Q==}
-    cpu: [arm64]
-    os: [linux]
+    libc: [musl]
 
   '@rollup/rollup-linux-arm64-gnu@4.37.0':
     resolution: {integrity: sha512-oNrJxcQT9IcbcmKlkF+Yz2tmOxZgG9D9GRq+1OE6XCQwCVwxixYAa38Z8qqPzQvzt1FCfmrHX03E0pWoXm1DqA==}
     cpu: [arm64]
     os: [linux]
-
-  '@rollup/rollup-linux-arm64-musl@4.34.2':
-    resolution: {integrity: sha512-UlFk+E46TZEoxD9ufLKDBzfSG7Ki03fo6hsNRRRHF+KuvNZ5vd1RRVQm8YZlGsjcJG8R252XFK0xNPay+4WV7w==}
-    cpu: [arm64]
-    os: [linux]
+    libc: [glibc]
 
   '@rollup/rollup-linux-arm64-musl@4.37.0':
     resolution: {integrity: sha512-pfxLBMls+28Ey2enpX3JvjEjaJMBX5XlPCZNGxj4kdJyHduPBXtxYeb8alo0a7bqOoWZW2uKynhHxF/MWoHaGQ==}
@@ -1284,96 +1240,58 @@ packages:
     os: [linux]
     libc: [musl]
 
-  '@rollup/rollup-linux-loongarch64-gnu@4.34.2':
-    resolution: {integrity: sha512-hJhfsD9ykx59jZuuoQgYT1GEcNNi3RCoEmbo5OGfG8RlHOiVS7iVNev9rhLKh7UBYq409f4uEw0cclTXx8nh8Q==}
-    cpu: [loong64]
-    os: [linux]
-
   '@rollup/rollup-linux-loongarch64-gnu@4.37.0':
     resolution: {integrity: sha512-yCE0NnutTC/7IGUq/PUHmoeZbIwq3KRh02e9SfFh7Vmc1Z7atuJRYWhRME5fKgT8aS20mwi1RyChA23qSyRGpA==}
     cpu: [loong64]
     os: [linux]
-
-  '@rollup/rollup-linux-powerpc64le-gnu@4.34.2':
-    resolution: {integrity: sha512-g/O5IpgtrQqPegvqopvmdCF9vneLE7eqYfdPWW8yjPS8f63DNam3U4ARL1PNNB64XHZDHKpvO2Giftf43puB8Q==}
-    cpu: [ppc64]
-    os: [linux]
+    libc: [glibc]
 
   '@rollup/rollup-linux-powerpc64le-gnu@4.37.0':
     resolution: {integrity: sha512-NxcICptHk06E2Lh3a4Pu+2PEdZ6ahNHuK7o6Np9zcWkrBMuv21j10SQDJW3C9Yf/A/P7cutWoC/DptNLVsZ0VQ==}
     cpu: [ppc64]
     os: [linux]
-
-  '@rollup/rollup-linux-riscv64-gnu@4.34.2':
-    resolution: {integrity: sha512-bSQijDC96M6PuooOuXHpvXUYiIwsnDmqGU8+br2U7iPoykNi9JtMUpN7K6xml29e0evK0/g0D1qbAUzWZFHY5Q==}
-    cpu: [riscv64]
-    os: [linux]
+    libc: [glibc]
 
   '@rollup/rollup-linux-riscv64-gnu@4.37.0':
     resolution: {integrity: sha512-PpWwHMPCVpFZLTfLq7EWJWvrmEuLdGn1GMYcm5MV7PaRgwCEYJAwiN94uBuZev0/J/hFIIJCsYw4nLmXA9J7Pw==}
     cpu: [riscv64]
     os: [linux]
+    libc: [glibc]
 
   '@rollup/rollup-linux-riscv64-musl@4.37.0':
     resolution: {integrity: sha512-DTNwl6a3CfhGTAOYZ4KtYbdS8b+275LSLqJVJIrPa5/JuIufWWZ/QFvkxp52gpmguN95eujrM68ZG+zVxa8zHA==}
     cpu: [riscv64]
     os: [linux]
-
-  '@rollup/rollup-linux-s390x-gnu@4.34.2':
-    resolution: {integrity: sha512-49TtdeVAsdRuiUHXPrFVucaP4SivazetGUVH8CIxVsNsaPHV4PFkpLmH9LeqU/R4Nbgky9lzX5Xe1NrzLyraVA==}
-    cpu: [s390x]
-    os: [linux]
+    libc: [musl]
 
   '@rollup/rollup-linux-s390x-gnu@4.37.0':
     resolution: {integrity: sha512-hZDDU5fgWvDdHFuExN1gBOhCuzo/8TMpidfOR+1cPZJflcEzXdCy1LjnklQdW8/Et9sryOPJAKAQRw8Jq7Tg+A==}
     cpu: [s390x]
     os: [linux]
-
-  '@rollup/rollup-linux-x64-gnu@4.34.2':
-    resolution: {integrity: sha512-j+jFdfOycLIQ7FWKka9Zd3qvsIyugg5LeZuHF6kFlXo6MSOc6R1w37YUVy8VpAKd81LMWGi5g9J25P09M0SSIw==}
-    cpu: [x64]
-    os: [linux]
+    libc: [glibc]
 
   '@rollup/rollup-linux-x64-gnu@4.37.0':
     resolution: {integrity: sha512-pKivGpgJM5g8dwj0ywBwe/HeVAUSuVVJhUTa/URXjxvoyTT/AxsLTAbkHkDHG7qQxLoW2s3apEIl26uUe08LVQ==}
     cpu: [x64]
     os: [linux]
-
-  '@rollup/rollup-linux-x64-musl@4.34.2':
-    resolution: {integrity: sha512-aDPHyM/D2SpXfSNCVWCxyHmOqN9qb7SWkY1+vaXqMNMXslZYnwh9V/UCudl6psyG0v6Ukj7pXanIpfZwCOEMUg==}
-    cpu: [x64]
-    os: [linux]
+    libc: [glibc]
 
   '@rollup/rollup-linux-x64-musl@4.37.0':
     resolution: {integrity: sha512-E2lPrLKE8sQbY/2bEkVTGDEk4/49UYRVWgj90MY8yPjpnGBQ+Xi1Qnr7b7UIWw1NOggdFQFOLZ8+5CzCiz143w==}
     cpu: [x64]
     os: [linux]
-
-  '@rollup/rollup-win32-arm64-msvc@4.34.2':
-    resolution: {integrity: sha512-LQRkCyUBnAo7r8dbEdtNU08EKLCJMgAk2oP5H3R7BnUlKLqgR3dUjrLBVirmc1RK6U6qhtDw29Dimeer8d5hzQ==}
-    cpu: [arm64]
-    os: [win32]
+    libc: [musl]
 
   '@rollup/rollup-win32-arm64-msvc@4.37.0':
     resolution: {integrity: sha512-Jm7biMazjNzTU4PrQtr7VS8ibeys9Pn29/1bm4ph7CP2kf21950LgN+BaE2mJ1QujnvOc6p54eWWiVvn05SOBg==}
     cpu: [arm64]
     os: [win32]
 
-  '@rollup/rollup-win32-ia32-msvc@4.34.2':
-    resolution: {integrity: sha512-wt8OhpQUi6JuPFkm1wbVi1BByeag87LDFzeKSXzIdGcX4bMLqORTtKxLoCbV57BHYNSUSOKlSL4BYYUghainYA==}
-    cpu: [ia32]
-    os: [win32]
-
   '@rollup/rollup-win32-ia32-msvc@4.37.0':
     resolution: {integrity: sha512-e3/1SFm1OjefWICB2Ucstg2dxYDkDTZGDYgwufcbsxTHyqQps1UQf33dFEChBNmeSsTOyrjw2JJq0zbG5GF6RA==}
     cpu: [ia32]
     os: [win32]
 
-  '@rollup/rollup-win32-x64-msvc@4.34.2':
-    resolution: {integrity: sha512-rUrqINax0TvrPBXrFKg0YbQx18NpPN3NNrgmaao9xRNbTwek7lOXObhx8tQy8gelmQ/gLaGy1WptpU2eKJZImg==}
-    cpu: [x64]
-    os: [win32]
-
   '@rollup/rollup-win32-x64-msvc@4.37.0':
     resolution: {integrity: sha512-LWbXUBwn/bcLx2sSsqy7pK5o+Nr+VCoRoAohfJ5C/aBio9nfJmGQqHAhU6pwxV/RmyTk5AqdySma7uwWGlmeuA==}
     cpu: [x64]
@@ -1464,6 +1382,9 @@ packages:
   '@tybys/wasm-util@0.9.0':
     resolution: {integrity: sha512-6+7nlbMVX/PVDCwaIQ8nTOPveOcFLSt8GcXdx8hD0bt39uWxYT88uXzqTd4fTvqta7oeUJqudepapKNt2DYJFw==}
 
+  '@types/connect@3.4.38':
+    resolution: {integrity: sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==}
+
   '@types/doctrine@0.0.9':
     resolution: {integrity: sha512-eOIHzCUSH7SMfonMG1LsC2f8vxBFtho6NGBznK41R84YzPuvSBzrhEps33IsQiOW9+VL6NQ9DbjQJznk/S4uRA==}
 
@@ -1571,21 +1492,25 @@ packages:
     resolution: {integrity: sha512-fp4Azi8kHz6TX8SFmKfyScZrMLfp++uRm2srpqRjsRZIIBzH74NtSkdEUHImR4G7f7XJ+sVZjCc6KDDK04YEpQ==}
     cpu: [arm64]
     os: [linux]
+    libc: [glibc]
 
   '@unrs/rspack-resolver-binding-linux-arm64-musl@1.2.2':
     resolution: {integrity: sha512-gMiG3DCFioJxdGBzhlL86KcFgt9HGz0iDhw0YVYPsShItpN5pqIkNrI+L/Q/0gfDiGrfcE0X3VANSYIPmqEAlQ==}
     cpu: [arm64]
     os: [linux]
+    libc: [musl]
 
   '@unrs/rspack-resolver-binding-linux-x64-gnu@1.2.2':
     resolution: {integrity: sha512-n/4n2CxaUF9tcaJxEaZm+lqvaw2gflfWQ1R9I7WQgYkKEKbRKbpG/R3hopYdUmLSRI4xaW1Cy0Bz40eS2Yi4Sw==}
     cpu: [x64]
     os: [linux]
+    libc: [glibc]
 
   '@unrs/rspack-resolver-binding-linux-x64-musl@1.2.2':
     resolution: {integrity: sha512-cHyhAr6rlYYbon1L2Ag449YCj3p6XMfcYTP0AQX+KkQo025d1y/VFtPWvjMhuEsE2lLvtHm7GdJozj6BOMtzVg==}
     cpu: [x64]
     os: [linux]
+    libc: [musl]
 
   '@unrs/rspack-resolver-binding-wasm32-wasi@1.2.2':
     resolution: {integrity: sha512-eogDKuICghDLGc32FtP+WniG38IB1RcGOGz0G3z8406dUdjJvxfHGuGs/dSlM9YEp/v0lEqhJ4mBu6X2nL9pog==}
@@ -1602,8 +1527,9 @@ packages:
     cpu: [x64]
     os: [win32]
 
-  '@vitejs/plugin-vue@5.2.3':
-    resolution: {integrity: sha512-IYSLEQj4LgZZuoVpdSUCw3dIynTWQgPlaRP6iAvMle4My0HdYwr5g5wQAfwOeHQBmYwEkqF70nRpSilr6PoUDg==}
+  '@vitejs/plugin-vue@https://pkg.pr.new/@vitejs/plugin-vue@c156992':
+    resolution: {tarball: https://pkg.pr.new/@vitejs/plugin-vue@c156992}
+    version: 5.2.1
     engines: {node: ^18.0.0 || >=20.0.0}
     peerDependencies:
       vite: ^5.0.0 || ^6.0.0
@@ -1648,6 +1574,9 @@ packages:
   '@vitest/pretty-format@3.0.9':
     resolution: {integrity: sha512-OW9F8t2J3AwFEwENg3yMyKWweF7oRJlMyHOMIhO5F3n0+cgQAJZBjNgrF8dLwFTEXl5jUqBLXd9QyyKv8zEcmA==}
 
+  '@vitest/pretty-format@3.1.1':
+    resolution: {integrity: sha512-dg0CIzNx+hMMYfNmSqJlLSXEmnNhMswcn3sXO7Tpldr0LiGmg3eXdLLhwkv2ZqgHb/d5xg5F7ezNFRA1fA13yA==}
+
   '@vitest/runner@3.0.9':
     resolution: {integrity: sha512-NX9oUXgF9HPfJSwl8tUZCMP1oGx2+Sf+ru6d05QjzQz4OwWg0psEzwY6VexP2tTHWdOkhKHUIZH+fS6nA7jfOw==}
 
@@ -1657,11 +1586,16 @@ packages:
   '@vitest/spy@3.0.9':
     resolution: {integrity: sha512-/CcK2UDl0aQ2wtkp3YVWldrpLRNCfVcIOFGlVGKO4R5eajsH393Z1yiXLVQ7vWsj26JOEjeZI0x5sm5P4OGUNQ==}
 
+  '@vitest/ui@3.1.1':
+    resolution: {integrity: sha512-2HpiRIYg3dlvAJBV9RtsVswFgUSJK4Sv7QhpxoP0eBGkYwzGIKP34PjaV00AULQi9Ovl6LGyZfsetxDWY5BQdQ==}
+    peerDependencies:
+      vitest: 3.1.1
+
   '@vitest/utils@3.0.9':
     resolution: {integrity: sha512-ilHM5fHhZ89MCp5aAaM9uhfl1c2JdxVxl3McqsdVyVNN6JffnEen8UMCdRTzOhGXNQGo5GNL9QugHrz727Wnng==}
 
-  '@vitest/utils@3.0.4':
-    resolution: {integrity: sha512-8BqC1ksYsHtbWH+DfpOAKrFw3jl3Uf9J7yeFh85Pz52IWuh1hBBtyfEbRNNZNjl8H8A5yMLH9/t+k7HIKzQcZQ==}
+  '@vitest/utils@3.1.1':
+    resolution: {integrity: sha512-1XIjflyaU2k3HMArJ50bwSh3wKWPD6Q47wz/NUSmRV0zNywPc4w79ARjg/i/aNINHwA+mIALhUVqD9/aUvZNgg==}
 
   '@vue/compiler-core@3.5.13':
     resolution: {integrity: sha512-oOdAkwqUfW1WqpwSYJce06wvt6HljgY3fGeM9NcVA1HaYOij3mZG9Rkysn0OHuyUAGMbEbARIpsG+LPVlBJ5/Q==}
@@ -1679,9 +1613,35 @@ packages:
     resolution: {integrity: sha512-oTyUE+QHIzLw2PpV14GD/c7EohDyP64xCniWTcqcEmTd699eFqTIwOmtDYjcO1j3QgdXoJEoWv1/cCdLrRoOfg==}
     engines: {node: '>= 0.12.0'}
 
+  '@vue/reactivity@3.5.13':
+    resolution: {integrity: sha512-NaCwtw8o48B9I6L1zl2p41OHo/2Z4wqYGGIK1Khu5T7yxrn+ATOixn/Udn2m+6kZKB/J7cuT9DbWWhRxqixACg==}
+
   '@vue/repl@4.5.1':
     resolution: {integrity: sha512-YYXvFue2GOrZ6EWnoA8yQVKzdCIn45+tpwJHzMof1uwrgyYAVY9ynxCsDYeAuWcpaAeylg/nybhFuqiFy2uvYA==}
 
+  '@vue/runtime-core@3.5.13':
+    resolution: {integrity: sha512-Fj4YRQ3Az0WTZw1sFe+QDb0aXCerigEpw418pw1HBUKFtnQHWzwojaukAs2X/c9DQz4MQ4bsXTGlcpGxU/RCIw==}
+
+  '@vue/runtime-dom@3.5.13':
+    resolution: {integrity: sha512-dLaj94s93NYLqjLiyFzVs9X6dWhTdAlEAciC3Moq7gzAc13VJUdCnjjRurNM6uTLFATRHexHCTu/Xp3eW6yoog==}
+
+  '@vue/server-renderer@3.5.13':
+    resolution: {integrity: sha512-wAi4IRJV/2SAW3htkTlB+dHeRmpTiVIK1OGLWV1yeStVSebSQQOwGwIq0D3ZIoBj2C2qpgz5+vX9iEBkTdk5YA==}
+    peerDependencies:
+      vue: 3.5.13
+
+  '@vue/shared@3.5.13':
+    resolution: {integrity: sha512-/hnE/qP5ZoGpol0a5mDi45bOd7t3tjYJBjsgCsivow7D48cJeV5l05RD82lPqi7gRiphZM37rnhW1l6ZoCNNnQ==}
+
+  '@vueuse/core@11.3.0':
+    resolution: {integrity: sha512-7OC4Rl1f9G8IT6rUfi9JrKiXy4bfmHhZ5x2Ceojy0jnd3mHNEvV4JaRygH362ror6/NZ+Nl+n13LPzGiPN8cKA==}
+
+  '@vueuse/metadata@11.3.0':
+    resolution: {integrity: sha512-pwDnDspTqtTo2HwfLw4Rp6yywuuBdYnPYDq+mO38ZYKGebCUQC/nVj/PXSiK9HX5otxLz8Fn7ECPbjiRz2CC3g==}
+
+  '@vueuse/shared@11.3.0':
+    resolution: {integrity: sha512-P8gSSWQeucH5821ek2mn/ciCk+MS/zoRKqdQIM3bHq6p7GXDAJLmnRRKmF5F65sAVJIfzQlwR3aDzwCn10s8hA==}
+
   '@zeit/schemas@2.36.0':
     resolution: {integrity: sha512-7kjMwcChYEzMKjeex9ZFXkt1AyNov9R5HZtjBKVsmVpw7pa7ZtlCGvCBC2vnnXctaYN+aRI61HjIqeetZW5ROg==}
 
@@ -1825,6 +1785,13 @@ packages:
   buffer-crc32@0.2.13:
     resolution: {integrity: sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==}
 
+  buffer-from@1.1.2:
+    resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==}
+
+  bundle-name@4.1.0:
+    resolution: {integrity: sha512-tjwM5exMg6BGRI+kNmTntNsvdZS1X8BFYS6tnJ2hdH0kVxM6/eVZ2xy+FqStSWvYmtfFMDLIxurorHwDKfDz5Q==}
+    engines: {node: '>=18'}
+
   bytes@3.0.0:
     resolution: {integrity: sha512-pMhOfFDPiv9t5jjIXkHosWmkSyQbvsgEVNkz0ERHbuLh2T/7j4Mqqpz523Fe8MVY89KC6Sh/QfS2sM+SjgFDcw==}
     engines: {node: '>= 0.8'}
@@ -2396,8 +2363,8 @@ packages:
   flatted@3.3.1:
     resolution: {integrity: sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw==}
 
-  flatted@3.3.2:
-    resolution: {integrity: sha512-AiwGJM8YcNOaobumgtng+6NHuOqC3A7MixFeDafM3X9cIUM+xUXoS5Vfgf+OihAYe20fxqNM9yPBXJzRtZ/4eA==}
+  flatted@3.3.3:
+    resolution: {integrity: sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==}
 
   foreground-child@3.3.0:
     resolution: {integrity: sha512-Ld2g8rrAyMYFXBhEqMz8ZAHBi4J4uS1i/CxGMDnjyFWddMXLVcDp051DZfu+t7+ab7Wv6SMqpWmyFIj5UbfFvg==}
@@ -3098,6 +3065,9 @@ packages:
   path-to-regexp@3.3.0:
     resolution: {integrity: sha512-qyCH421YQPS2WFDxDjftfc1ZR5WKQzVzqsp4n9M2kQhVOo/ByahFoUNJfl58kOcEGfQ//7weFTDhm+ss8Ecxgw==}
 
+  pathe@1.1.2:
+    resolution: {integrity: sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ==}
+
   pathe@2.0.3:
     resolution: {integrity: sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==}
 
@@ -3167,10 +3137,6 @@ packages:
   postcss-value-parser@4.2.0:
     resolution: {integrity: sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==}
 
-  postcss@8.5.1:
-    resolution: {integrity: sha512-6oz2beyjc5VMn/KV1pPw8fliQkhBXrVn1Z3TVyqZxU8kZpzEKhBdmCFqI6ZbmGtamQvQGuU1sgPTk8ZrXDD7jQ==}
-    engines: {node: ^10 || ^12 || >=14}
-
   postcss@8.5.3:
     resolution: {integrity: sha512-dle9A3yYxlBSrt8Fu+IpjGT8SY8hN0mlaA6GY8t0P5PjIOZemULz/E2Bnm/2dcUOena75OTNkHI76uZBNUUq3A==}
     engines: {node: ^10 || ^12 || >=14}
@@ -3351,11 +3317,6 @@ packages:
     peerDependencies:
       rollup: ^1.20.0 || ^2.0.0 || ^3.0.0 || ^4.0.0
 
-  rollup@4.34.2:
-    resolution: {integrity: sha512-sBDUoxZEaqLu9QeNalL8v3jw6WjPku4wfZGyTU7l7m1oC+rpRihXc/n/H+4148ZkGz5Xli8CHMns//fFGKvpIQ==}
-    engines: {node: '>=18.0.0', npm: '>=8.0.0'}
-    hasBin: true
-
   rollup@4.37.0:
     resolution: {integrity: sha512-iAtQy/L4QFU+rTJ1YUjXqJOJzuwEghqWzCEYD2FEghT7Gsy1VdABntrO4CLopA5IkflTyqNiLNwPcOJ3S7UKLg==}
     engines: {node: '>=18.0.0', npm: '>=8.0.0'}
@@ -3367,6 +3328,10 @@ packages:
   rspack-resolver@1.2.2:
     resolution: {integrity: sha512-Fwc19jMBA3g+fxDJH2B4WxwZjE0VaaOL7OX/A4Wn5Zv7bOD/vyPZhzXfaO73Xc2GAlfi96g5fGUa378WbIGfFw==}
 
+  run-applescript@7.0.0:
+    resolution: {integrity: sha512-9by4Ij99JUr/MCFBUkDKLWK3G9HVXmabKz9U5MlIAIuvuzkiOicRYs8XJLxX+xahD+mLiiCYDqF9dKAgtzKP1A==}
+    engines: {node: '>=18'}
+
   run-parallel@1.2.0:
     resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==}
 
@@ -3437,8 +3402,8 @@ packages:
     resolution: {integrity: sha512-94Bdh3cC2PKrbgSOUqTiGPWVZeSiXfKOVZNJniWoqrWrRkB1CJzBU3NEbiTsPcYy1lDsANA/THzS+9WBiy5nfQ==}
     engines: {node: '>= 10'}
 
-  sirv@3.0.0:
-    resolution: {integrity: sha512-BPwJGUeDaDCHihkORDchNyyTvWFhcusy1XMmhEVTQTwGeybFbp8YEmB+njbPnth1FibULBSBVwCQni25XlCUDg==}
+  sirv@3.0.1:
+    resolution: {integrity: sha512-FoqMu0NCGBLCcAkS1qA+XJIQTR6/JHfQXl+uGteNCQ76T91DMUjPa9xfmeqMY3z80nLSg9yQmNjK0Px6RWsH/A==}
     engines: {node: '>=18'}
 
   slice-ansi@5.0.0:
@@ -3596,8 +3561,8 @@ packages:
   tinyexec@0.3.2:
     resolution: {integrity: sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA==}
 
-  tinyglobby@0.2.10:
-    resolution: {integrity: sha512-Zc+8eJlFMvgatPZTl6A9L/yht8QqdmUNtURHaKZLmKBE12hNPSrqNkUp2cs3M/UKmNVVAMFQYSjYIVHDjW5zew==}
+  tinyglobby@0.2.12:
+    resolution: {integrity: sha512-qkf4trmKSIiMTs/E63cxH+ojC2unam7rJ0WrauAzpT3ECNTxGRMlaXxVbfxMUC/w0LaYk6jQ4y/nGR9uBO3tww==}
     engines: {node: '>=12.0.0'}
 
   tinypool@1.0.2:
@@ -3699,6 +3664,10 @@ packages:
     resolution: {integrity: sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==}
     engines: {node: '>= 10.0.0'}
 
+  unpipe@1.0.0:
+    resolution: {integrity: sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==}
+    engines: {node: '>= 0.8'}
+
   unplugin-utils@0.2.4:
     resolution: {integrity: sha512-8U/MtpkPkkk3Atewj1+RcKIjb5WBimZ/WSLhhR3w6SsIj8XJuKTacSP8g+2JhfSGw0Cb125Y+2zA/IzJZDVbhA==}
     engines: {node: '>=18.12.0'}
@@ -3723,15 +3692,67 @@ packages:
     resolution: {integrity: sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==}
     engines: {node: '>= 0.8'}
 
+  vite-hyper-config@0.4.1:
+    resolution: {integrity: sha512-w9D4g0+5Km8XCgkBY/BZrXZAl8FF2q1UpDXT/Fsm6VLEU5tkkzDCko8fjLPOaSbvirUJgbY5OsD5wuuZ6581Fg==}
+    engines: {node: '>=18.0.0'}
+    peerDependencies:
+      vite: ^4.0.0 || ^5.0.0 || ^6.0.0
+
+  vite-node@2.1.9:
+    resolution: {integrity: sha512-AM9aQ/IPrW/6ENLQg3AGY4K1N2TGZdR5e4gu/MmmR2xR3Ll1+dib+nook92g4TV3PXVyeyxdWwtaCAiUL0hMxA==}
+    engines: {node: ^18.0.0 || >=20.0.0}
+    hasBin: true
+
   vite-node@3.0.9:
     resolution: {integrity: sha512-w3Gdx7jDcuT9cNn9jExXgOyKmf5UOTb6WMHz8LGAm54eS1Elf5OuBhCxl6zJxGhEeIkgsE1WbHuoL0mj/UXqXg==}
     engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0}
     hasBin: true
 
+  vite-plugin-inspect@0.8.7:
+    resolution: {integrity: sha512-/XXou3MVc13A5O9/2Nd6xczjrUwt7ZyI9h8pTnUMkr5SshLcb0PJUOVq2V+XVkdeU4njsqAtmK87THZuO2coGA==}
+    engines: {node: '>=14'}
+    peerDependencies:
+      '@nuxt/kit': '*'
+      vite: ^3.1.0 || ^4.0.0 || ^5.0.0-0
+    peerDependenciesMeta:
+      '@nuxt/kit':
+        optional: true
+
   vite@5.4.14:
     resolution: {integrity: sha512-EK5cY7Q1D8JNhSaPKVK4pwBFvaTmZxEnoKXLG/U9gmdDcihQGNzFlgIvaxezFR4glP1LsuiedwMBqCXH3wZccA==}
     engines: {node: ^18.0.0 || >=20.0.0}
     hasBin: true
+    peerDependencies:
+      '@types/node': ^18.0.0 || >=20.0.0
+      less: '*'
+      lightningcss: ^1.21.0
+      sass: '*'
+      sass-embedded: '*'
+      stylus: '*'
+      sugarss: '*'
+      terser: ^5.4.0
+    peerDependenciesMeta:
+      '@types/node':
+        optional: true
+      less:
+        optional: true
+      lightningcss:
+        optional: true
+      sass:
+        optional: true
+      sass-embedded:
+        optional: true
+      stylus:
+        optional: true
+      sugarss:
+        optional: true
+      terser:
+        optional: true
+
+  vite@6.2.4:
+    resolution: {integrity: sha512-veHMSew8CcRzhL5o8ONjy8gkfmFJAd5Ac16oxBUjlwgX3Gq2Wqr+qNC3TjPIpy7TPV/KporLga5GT9HqdrCizw==}
+    engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0}
+    hasBin: true
     peerDependencies:
       '@types/node': ^18.0.0 || ^20.0.0 || >=22.0.0
       jiti: '>=1.21.0'
@@ -4355,6 +4376,8 @@ snapshots:
   '@pkgjs/parseargs@0.11.0':
     optional: true
 
+  '@polka/url@1.0.0-next.28': {}
+
   '@puppeteer/browsers@2.8.0':
     dependencies:
       debug: 4.4.0
@@ -4422,120 +4445,63 @@ snapshots:
     optionalDependencies:
       rollup: 4.37.0
 
-  '@rollup/rollup-android-arm-eabi@4.34.2':
-    optional: true
-
   '@rollup/rollup-android-arm-eabi@4.37.0':
     optional: true
 
-  '@rollup/rollup-android-arm64@4.34.2':
-    optional: true
-
   '@rollup/rollup-android-arm64@4.37.0':
     optional: true
 
-  '@rollup/rollup-darwin-arm64@4.34.2':
-    optional: true
-
   '@rollup/rollup-darwin-arm64@4.37.0':
     optional: true
 
-  '@rollup/rollup-darwin-x64@4.34.2':
-    optional: true
-
   '@rollup/rollup-darwin-x64@4.37.0':
     optional: true
 
-  '@rollup/rollup-freebsd-arm64@4.34.2':
-    optional: true
-
   '@rollup/rollup-freebsd-arm64@4.37.0':
     optional: true
 
-  '@rollup/rollup-freebsd-x64@4.34.2':
-    optional: true
-
   '@rollup/rollup-freebsd-x64@4.37.0':
     optional: true
 
-  '@rollup/rollup-linux-arm-gnueabihf@4.34.2':
-    optional: true
-
   '@rollup/rollup-linux-arm-gnueabihf@4.37.0':
     optional: true
 
-  '@rollup/rollup-linux-arm-musleabihf@4.34.2':
-    optional: true
-
   '@rollup/rollup-linux-arm-musleabihf@4.37.0':
     optional: true
 
-  '@rollup/rollup-linux-arm64-gnu@4.34.2':
-    optional: true
-
   '@rollup/rollup-linux-arm64-gnu@4.37.0':
     optional: true
 
-  '@rollup/rollup-linux-arm64-musl@4.34.2':
-    optional: true
-
   '@rollup/rollup-linux-arm64-musl@4.37.0':
     optional: true
 
-  '@rollup/rollup-linux-loongarch64-gnu@4.34.2':
-    optional: true
-
   '@rollup/rollup-linux-loongarch64-gnu@4.37.0':
     optional: true
 
-  '@rollup/rollup-linux-powerpc64le-gnu@4.34.2':
-    optional: true
-
   '@rollup/rollup-linux-powerpc64le-gnu@4.37.0':
     optional: true
 
-  '@rollup/rollup-linux-riscv64-gnu@4.34.2':
-    optional: true
-
   '@rollup/rollup-linux-riscv64-gnu@4.37.0':
     optional: true
 
   '@rollup/rollup-linux-riscv64-musl@4.37.0':
     optional: true
 
-  '@rollup/rollup-linux-s390x-gnu@4.34.2':
-    optional: true
-
   '@rollup/rollup-linux-s390x-gnu@4.37.0':
     optional: true
 
-  '@rollup/rollup-linux-x64-gnu@4.34.2':
-    optional: true
-
   '@rollup/rollup-linux-x64-gnu@4.37.0':
     optional: true
 
-  '@rollup/rollup-linux-x64-musl@4.34.2':
-    optional: true
-
   '@rollup/rollup-linux-x64-musl@4.37.0':
     optional: true
 
-  '@rollup/rollup-win32-arm64-msvc@4.34.2':
-    optional: true
-
   '@rollup/rollup-win32-arm64-msvc@4.37.0':
     optional: true
 
-  '@rollup/rollup-win32-ia32-msvc@4.34.2':
-    optional: true
-
   '@rollup/rollup-win32-ia32-msvc@4.37.0':
     optional: true
 
-  '@rollup/rollup-win32-x64-msvc@4.34.2':
-    optional: true
-
   '@rollup/rollup-win32-x64-msvc@4.37.0':
     optional: true
 
@@ -4598,6 +4564,10 @@ snapshots:
       tslib: 2.8.1
     optional: true
 
+  '@types/connect@3.4.38':
+    dependencies:
+      '@types/node': 22.13.13
+
   '@types/doctrine@0.0.9': {}
 
   '@types/estree@1.0.6': {}
@@ -4741,12 +4711,17 @@ snapshots:
   '@unrs/rspack-resolver-binding-win32-x64-msvc@1.2.2':
     optional: true
 
-  '@vitejs/plugin-vue@5.2.3(vite@5.4.14(@types/node@22.13.13)(sass@1.86.0))(vue@packages+vue)':
+  '@vitejs/plugin-vue@https://pkg.pr.new/@vitejs/plugin-vue@c156992(vite@6.2.4(@types/node@22.13.13)(sass@1.86.0)(terser@5.33.0)(yaml@2.7.0))(vue@3.5.13(typescript@5.6.2))':
     dependencies:
-      vite: 5.4.14(@types/node@22.13.13)(sass@1.86.0)
+      vite: 6.2.4(@types/node@22.13.13)(sass@1.86.0)(terser@5.33.0)(yaml@2.7.0)
+      vue: 3.5.13(typescript@5.6.2)
+
+  '@vitejs/plugin-vue@https://pkg.pr.new/@vitejs/plugin-vue@c156992(vite@6.2.4(@types/node@22.13.13)(sass@1.86.0)(terser@5.33.0)(yaml@2.7.0))(vue@packages+vue)':
+    dependencies:
+      vite: 6.2.4(@types/node@22.13.13)(sass@1.86.0)(terser@5.33.0)(yaml@2.7.0)
       vue: link:packages/vue
 
-  '@vitest/coverage-v8@3.0.9(vitest@3.0.9(@types/node@22.13.13)(jsdom@26.0.0)(sass@1.86.0))':
+  '@vitest/coverage-v8@3.0.9(vitest@3.0.9)':
     dependencies:
       '@ampproject/remapping': 2.3.0
       '@bcoe/v8-coverage': 1.0.2
@@ -4760,17 +4735,17 @@ snapshots:
       std-env: 3.8.0
       test-exclude: 7.0.1
       tinyrainbow: 2.0.0
-      vitest: 3.0.9(@types/node@22.13.13)(jsdom@26.0.0)(sass@1.86.0)
+      vitest: 3.0.9(@types/node@22.13.13)(@vitest/ui@3.1.1)(jsdom@26.0.0)(sass@1.86.0)(terser@5.33.0)(yaml@2.7.0)
     transitivePeerDependencies:
       - supports-color
 
-  '@vitest/eslint-plugin@1.1.38(@typescript-eslint/utils@8.27.0(eslint@9.23.0)(typescript@5.6.2))(eslint@9.23.0)(typescript@5.6.2)(vitest@3.0.9(@types/node@22.13.13)(jsdom@26.0.0)(sass@1.86.0))':
+  '@vitest/eslint-plugin@1.1.38(@typescript-eslint/utils@8.27.0(eslint@9.23.0)(typescript@5.6.2))(eslint@9.23.0)(typescript@5.6.2)(vitest@3.0.9)':
     dependencies:
       '@typescript-eslint/utils': 8.27.0(eslint@9.23.0)(typescript@5.6.2)
       eslint: 9.23.0
     optionalDependencies:
       typescript: 5.6.2
-      vitest: 3.0.9(@types/node@22.13.13)(jsdom@26.0.0)(sass@1.86.0)
+      vitest: 3.0.9(@types/node@22.13.13)(@vitest/ui@3.1.1)(jsdom@26.0.0)(sass@1.86.0)(terser@5.33.0)(yaml@2.7.0)
 
   '@vitest/expect@3.0.9':
     dependencies:
@@ -4779,18 +4754,22 @@ snapshots:
       chai: 5.2.0
       tinyrainbow: 2.0.0
 
-  '@vitest/mocker@3.0.9(vite@5.4.14(@types/node@22.13.13)(sass@1.86.0))':
+  '@vitest/mocker@3.0.9(vite@6.2.4(@types/node@22.13.13)(sass@1.86.0)(terser@5.33.0)(yaml@2.7.0))':
     dependencies:
       '@vitest/spy': 3.0.9
       estree-walker: 3.0.3
       magic-string: 0.30.17
     optionalDependencies:
-      vite: 5.4.14(@types/node@22.13.13)(sass@1.86.0)
+      vite: 6.2.4(@types/node@22.13.13)(sass@1.86.0)(terser@5.33.0)(yaml@2.7.0)
 
   '@vitest/pretty-format@3.0.9':
     dependencies:
       tinyrainbow: 2.0.0
 
+  '@vitest/pretty-format@3.1.1':
+    dependencies:
+      tinyrainbow: 2.0.0
+
   '@vitest/runner@3.0.9':
     dependencies:
       '@vitest/utils': 3.0.9
@@ -4806,21 +4785,32 @@ snapshots:
     dependencies:
       tinyspy: 3.0.2
 
+  '@vitest/ui@3.1.1(vitest@3.0.9)':
+    dependencies:
+      '@vitest/utils': 3.1.1
+      fflate: 0.8.2
+      flatted: 3.3.3
+      pathe: 2.0.3
+      sirv: 3.0.1
+      tinyglobby: 0.2.12
+      tinyrainbow: 2.0.0
+      vitest: 3.0.9(@types/node@22.13.13)(@vitest/ui@3.1.1)(jsdom@26.0.0)(sass@1.86.0)(terser@5.33.0)(yaml@2.7.0)
+
   '@vitest/utils@3.0.9':
     dependencies:
       '@vitest/pretty-format': 3.0.9
       loupe: 3.1.3
       tinyrainbow: 2.0.0
 
-  '@vitest/utils@3.0.4':
+  '@vitest/utils@3.1.1':
     dependencies:
-      '@vitest/pretty-format': 3.0.4
-      loupe: 3.1.2
+      '@vitest/pretty-format': 3.1.1
+      loupe: 3.1.3
       tinyrainbow: 2.0.0
 
   '@vue/compiler-core@3.5.13':
     dependencies:
-      '@babel/parser': 7.26.2
+      '@babel/parser': 7.26.10
       '@vue/shared': 3.5.13
       entities: 4.5.0
       estree-walker: 2.0.2
@@ -4833,14 +4823,14 @@ snapshots:
 
   '@vue/compiler-sfc@3.5.13':
     dependencies:
-      '@babel/parser': 7.26.2
+      '@babel/parser': 7.26.10
       '@vue/compiler-core': 3.5.13
       '@vue/compiler-dom': 3.5.13
       '@vue/compiler-ssr': 3.5.13
       '@vue/shared': 3.5.13
       estree-walker: 2.0.2
       magic-string: 0.30.17
-      postcss: 8.5.1
+      postcss: 8.5.3
       source-map-js: 1.2.1
 
   '@vue/compiler-ssr@3.5.13':
@@ -4850,8 +4840,51 @@ snapshots:
 
   '@vue/consolidate@1.0.0': {}
 
+  '@vue/reactivity@3.5.13':
+    dependencies:
+      '@vue/shared': 3.5.13
+
   '@vue/repl@4.5.1': {}
 
+  '@vue/runtime-core@3.5.13':
+    dependencies:
+      '@vue/reactivity': 3.5.13
+      '@vue/shared': 3.5.13
+
+  '@vue/runtime-dom@3.5.13':
+    dependencies:
+      '@vue/reactivity': 3.5.13
+      '@vue/runtime-core': 3.5.13
+      '@vue/shared': 3.5.13
+      csstype: 3.1.3
+
+  '@vue/server-renderer@3.5.13(vue@3.5.13(typescript@5.6.2))':
+    dependencies:
+      '@vue/compiler-ssr': 3.5.13
+      '@vue/shared': 3.5.13
+      vue: 3.5.13(typescript@5.6.2)
+
+  '@vue/shared@3.5.13': {}
+
+  '@vueuse/core@11.3.0(vue@packages+vue)':
+    dependencies:
+      '@types/web-bluetooth': 0.0.20
+      '@vueuse/metadata': 11.3.0
+      '@vueuse/shared': 11.3.0(vue@packages+vue)
+      vue-demi: 0.14.10(vue@packages+vue)
+    transitivePeerDependencies:
+      - '@vue/composition-api'
+      - vue
+
+  '@vueuse/metadata@11.3.0': {}
+
+  '@vueuse/shared@11.3.0(vue@packages+vue)':
+    dependencies:
+      vue-demi: 0.14.10(vue@packages+vue)
+    transitivePeerDependencies:
+      - '@vue/composition-api'
+      - vue
+
   '@zeit/schemas@2.36.0': {}
 
   accepts@1.3.8:
@@ -4988,6 +5021,13 @@ snapshots:
 
   buffer-crc32@0.2.13: {}
 
+  buffer-from@1.1.2:
+    optional: true
+
+  bundle-name@4.1.0:
+    dependencies:
+      run-applescript: 7.0.0
+
   bytes@3.0.0: {}
 
   cac@6.7.14: {}
@@ -5636,7 +5676,7 @@ snapshots:
 
   flatted@3.3.1: {}
 
-  flatted@3.3.2: {}
+  flatted@3.3.3: {}
 
   foreground-child@3.3.0:
     dependencies:
@@ -6326,6 +6366,8 @@ snapshots:
 
   path-to-regexp@3.3.0: {}
 
+  pathe@1.1.2: {}
+
   pathe@2.0.3: {}
 
   pathval@2.0.0: {}
@@ -6387,12 +6429,6 @@ snapshots:
 
   postcss-value-parser@4.2.0: {}
 
-  postcss@8.5.1:
-    dependencies:
-      nanoid: 3.3.8
-      picocolors: 1.1.1
-      source-map-js: 1.2.1
-
   postcss@8.5.3:
     dependencies:
       nanoid: 3.3.8
@@ -6634,31 +6670,6 @@ snapshots:
       '@rollup/plugin-inject': 5.0.5(rollup@4.37.0)
       rollup: 4.37.0
 
-  rollup@4.34.2:
-    dependencies:
-      '@types/estree': 1.0.6
-    optionalDependencies:
-      '@rollup/rollup-android-arm-eabi': 4.34.2
-      '@rollup/rollup-android-arm64': 4.34.2
-      '@rollup/rollup-darwin-arm64': 4.34.2
-      '@rollup/rollup-darwin-x64': 4.34.2
-      '@rollup/rollup-freebsd-arm64': 4.34.2
-      '@rollup/rollup-freebsd-x64': 4.34.2
-      '@rollup/rollup-linux-arm-gnueabihf': 4.34.2
-      '@rollup/rollup-linux-arm-musleabihf': 4.34.2
-      '@rollup/rollup-linux-arm64-gnu': 4.34.2
-      '@rollup/rollup-linux-arm64-musl': 4.34.2
-      '@rollup/rollup-linux-loongarch64-gnu': 4.34.2
-      '@rollup/rollup-linux-powerpc64le-gnu': 4.34.2
-      '@rollup/rollup-linux-riscv64-gnu': 4.34.2
-      '@rollup/rollup-linux-s390x-gnu': 4.34.2
-      '@rollup/rollup-linux-x64-gnu': 4.34.2
-      '@rollup/rollup-linux-x64-musl': 4.34.2
-      '@rollup/rollup-win32-arm64-msvc': 4.34.2
-      '@rollup/rollup-win32-ia32-msvc': 4.34.2
-      '@rollup/rollup-win32-x64-msvc': 4.34.2
-      fsevents: 2.3.3
-
   rollup@4.37.0:
     dependencies:
       '@types/estree': 1.0.6
@@ -6701,6 +6712,8 @@ snapshots:
       '@unrs/rspack-resolver-binding-win32-arm64-msvc': 1.2.2
       '@unrs/rspack-resolver-binding-win32-x64-msvc': 1.2.2
 
+  run-applescript@7.0.0: {}
+
   run-parallel@1.2.0:
     dependencies:
       queue-microtask: 1.2.3
@@ -6780,13 +6793,13 @@ snapshots:
 
   sirv@2.0.4:
     dependencies:
-      '@polka/url': 1.0.0-next.25
+      '@polka/url': 1.0.0-next.28
       mrmime: 2.0.0
       totalist: 3.0.1
 
-  sirv@3.0.0:
+  sirv@3.0.1:
     dependencies:
-      '@polka/url': 1.0.0-next.25
+      '@polka/url': 1.0.0-next.28
       mrmime: 2.0.0
       totalist: 3.0.1
 
@@ -6953,7 +6966,7 @@ snapshots:
 
   tinyexec@0.3.2: {}
 
-  tinyglobby@0.2.10:
+  tinyglobby@0.2.12:
     dependencies:
       fdir: 6.4.3(picomatch@4.0.2)
       picomatch: 4.0.2
@@ -7027,6 +7040,8 @@ snapshots:
 
   universalify@2.0.1: {}
 
+  unpipe@1.0.0: {}
+
   unplugin-utils@0.2.4:
     dependencies:
       pathe: 2.0.3
@@ -7052,13 +7067,48 @@ snapshots:
 
   vary@1.1.2: {}
 
-  vite-node@3.0.9(@types/node@22.13.13)(sass@1.86.0):
+  vite-hyper-config@0.4.1(@types/node@22.13.13)(sass@1.86.0)(terser@5.33.0)(vite@6.2.4(@types/node@22.13.13)(sass@1.86.0)(terser@5.33.0)(yaml@2.7.0)):
+    dependencies:
+      cac: 6.7.14
+      picocolors: 1.1.1
+      vite: 6.2.4(@types/node@22.13.13)(sass@1.86.0)(terser@5.33.0)(yaml@2.7.0)
+      vite-node: 2.1.9(@types/node@22.13.13)(sass@1.86.0)(terser@5.33.0)
+    transitivePeerDependencies:
+      - '@types/node'
+      - less
+      - lightningcss
+      - sass
+      - sass-embedded
+      - stylus
+      - sugarss
+      - supports-color
+      - terser
+
+  vite-node@2.1.9(@types/node@22.13.13)(sass@1.86.0)(terser@5.33.0):
+    dependencies:
+      cac: 6.7.14
+      debug: 4.4.0
+      es-module-lexer: 1.6.0
+      pathe: 1.1.2
+      vite: 5.4.14(@types/node@22.13.13)(sass@1.86.0)(terser@5.33.0)
+    transitivePeerDependencies:
+      - '@types/node'
+      - less
+      - lightningcss
+      - sass
+      - sass-embedded
+      - stylus
+      - sugarss
+      - supports-color
+      - terser
+
+  vite-node@3.0.9(@types/node@22.13.13)(sass@1.86.0)(terser@5.33.0)(yaml@2.7.0):
     dependencies:
       cac: 6.7.14
       debug: 4.4.0
       es-module-lexer: 1.6.0
       pathe: 2.0.3
-      vite: 5.4.14(@types/node@22.13.13)(sass@1.86.0)
+      vite: 6.2.4(@types/node@22.13.13)(sass@1.86.0)(terser@5.33.0)(yaml@2.7.0)
     transitivePeerDependencies:
       - '@types/node'
       - jiti
@@ -7073,10 +7123,10 @@ snapshots:
       - tsx
       - yaml
 
-  vite-plugin-inspect@0.8.7(rollup@4.31.0)(vite@6.1.0(@types/node@22.10.7)(sass@1.83.4)(terser@5.33.0)(yaml@2.6.1)):
+  vite-plugin-inspect@0.8.7(rollup@4.37.0)(vite@6.2.4(@types/node@22.13.13)(sass@1.86.0)(terser@5.33.0)(yaml@2.7.0)):
     dependencies:
       '@antfu/utils': 0.7.10
-      '@rollup/pluginutils': 5.1.0(rollup@4.31.0)
+      '@rollup/pluginutils': 5.1.0(rollup@4.37.0)
       debug: 4.4.0
       error-stack-parser-es: 0.1.5
       fs-extra: 11.2.0
@@ -7084,25 +7134,38 @@ snapshots:
       perfect-debounce: 1.0.0
       picocolors: 1.1.1
       sirv: 2.0.4
-      vite: 6.1.0(@types/node@22.10.7)(sass@1.83.4)(terser@5.33.0)(yaml@2.6.1)
+      vite: 6.2.4(@types/node@22.13.13)(sass@1.86.0)(terser@5.33.0)(yaml@2.7.0)
     transitivePeerDependencies:
       - rollup
       - supports-color
 
-  vite@5.4.14(@types/node@22.13.13)(sass@1.86.0):
+  vite@5.4.14(@types/node@22.13.13)(sass@1.86.0)(terser@5.33.0):
     dependencies:
-      esbuild: 0.24.2
-      postcss: 8.5.1
-      rollup: 4.34.2
+      esbuild: 0.21.5
+      postcss: 8.5.3
+      rollup: 4.37.0
     optionalDependencies:
       '@types/node': 22.13.13
       fsevents: 2.3.3
       sass: 1.86.0
+      terser: 5.33.0
+
+  vite@6.2.4(@types/node@22.13.13)(sass@1.86.0)(terser@5.33.0)(yaml@2.7.0):
+    dependencies:
+      esbuild: 0.25.1
+      postcss: 8.5.3
+      rollup: 4.37.0
+    optionalDependencies:
+      '@types/node': 22.13.13
+      fsevents: 2.3.3
+      sass: 1.86.0
+      terser: 5.33.0
+      yaml: 2.7.0
 
-  vitest@3.0.9(@types/node@22.13.13)(jsdom@26.0.0)(sass@1.86.0):
+  vitest@3.0.9(@types/node@22.13.13)(@vitest/ui@3.1.1)(jsdom@26.0.0)(sass@1.86.0)(terser@5.33.0)(yaml@2.7.0):
     dependencies:
       '@vitest/expect': 3.0.9
-      '@vitest/mocker': 3.0.9(vite@5.4.14(@types/node@22.13.13)(sass@1.86.0))
+      '@vitest/mocker': 3.0.9(vite@6.2.4(@types/node@22.13.13)(sass@1.86.0)(terser@5.33.0)(yaml@2.7.0))
       '@vitest/pretty-format': 3.0.9
       '@vitest/runner': 3.0.9
       '@vitest/snapshot': 3.0.9
@@ -7118,11 +7181,12 @@ snapshots:
       tinyexec: 0.3.2
       tinypool: 1.0.2
       tinyrainbow: 2.0.0
-      vite: 5.4.14(@types/node@22.13.13)(sass@1.86.0)
-      vite-node: 3.0.9(@types/node@22.13.13)(sass@1.86.0)
+      vite: 6.2.4(@types/node@22.13.13)(sass@1.86.0)(terser@5.33.0)(yaml@2.7.0)
+      vite-node: 3.0.9(@types/node@22.13.13)(sass@1.86.0)(terser@5.33.0)(yaml@2.7.0)
       why-is-node-running: 2.3.0
     optionalDependencies:
       '@types/node': 22.13.13
+      '@vitest/ui': 3.1.1(vitest@3.0.9)
       jsdom: 26.0.0
     transitivePeerDependencies:
       - jiti