diff --git a/config/vue3migration/compiler.js b/config/vue3migration/compiler.js
index a2c82584227f8e5d67b302755b1639fb0a21749a..d6b6e1e7533a7c223f95b1d4294e664d73306c51 100644
--- a/config/vue3migration/compiler.js
+++ b/config/vue3migration/compiler.js
@@ -2,16 +2,20 @@ const { parse, compile: compilerDomCompile } = require('@vue/compiler-dom');
 
 const COMMENT_NODE_TYPE = 3;
 
-const getPropIndex = (node, prop) => node.props?.findIndex((p) => p.name === prop) ?? -1;
+const hasProp = (node, prop) => node.props?.some((p) => p.name === prop);
 
 function modifyKeysInsideTemplateTag(templateNode) {
+  if (!templateNode.tag === 'template' || !hasProp(templateNode, 'for')) {
+    return;
+  }
+
   let keyCandidate = null;
   for (const node of templateNode.children) {
     const keyBindingIndex = node.props
       ? node.props.findIndex((prop) => prop.arg && prop.arg.content === 'key')
       : -1;
 
-    if (keyBindingIndex !== -1 && getPropIndex(node, 'for') === -1) {
+    if (keyBindingIndex !== -1 && !hasProp(node, 'for')) {
       if (!keyCandidate) {
         keyCandidate = node.props[keyBindingIndex];
       }
@@ -24,40 +28,97 @@ function modifyKeysInsideTemplateTag(templateNode) {
   }
 }
 
+function getSlotName(node) {
+  return node?.props?.find((prop) => prop.name === 'slot')?.arg?.content;
+}
+
+function filterCommentNodeAndTrailingSpace(node, idx, list) {
+  if (node.type === COMMENT_NODE_TYPE) {
+    return false;
+  }
+
+  if (node.content !== ' ') {
+    return true;
+  }
+
+  if (list[idx - 1]?.type === COMMENT_NODE_TYPE) {
+    return false;
+  }
+
+  return true;
+}
+
+function filterCommentNodes(node) {
+  const { length: originalLength } = node.children;
+  // eslint-disable-next-line no-param-reassign
+  node.children = node.children.filter(filterCommentNodeAndTrailingSpace);
+  if (node.children.length !== originalLength) {
+    // trim remaining spaces
+    while (node.children.at(-1)?.content === ' ') {
+      node.children.pop();
+    }
+  }
+}
+
+function dropVOnceForChildrenInsideVIfBecauseOfIssue7725(node) {
+  // See https://github.com/vuejs/core/issues/7725 for details
+  if (!hasProp(node, 'if')) {
+    return;
+  }
+
+  node.children?.forEach((child) => {
+    if (Array.isArray(child.props)) {
+      // eslint-disable-next-line no-param-reassign
+      child.props = child.props.filter((prop) => prop.name !== 'once');
+    }
+  });
+}
+
+function fixSameSlotsInsideTemplateFailingWhenUsingWhitespacePreserveDueToIssue6063(node) {
+  // See https://github.com/vuejs/core/issues/6063 for details
+  // eslint-disable-next-line no-param-reassign
+  node.children = node.children.filter((child, idx) => {
+    if (child.content !== ' ') {
+      // We need to drop only comment nodes
+      return true;
+    }
+
+    const previousNodeSlotName = getSlotName(node.children[idx - 1]);
+    const nextNodeSlotName = getSlotName(node.children[idx + 1]);
+
+    if (previousNodeSlotName && previousNodeSlotName === nextNodeSlotName) {
+      // We have a space beween two slot entries with same slot name, we need to drop it
+      return false;
+    }
+
+    return true;
+  });
+}
+
 module.exports = {
   parse,
   compile(template, options) {
     const rootNode = parse(template, options);
 
-    // We do not want to switch to whitespace: collapse mode which is Vue.js 3 default
-    // It will be too devastating to codebase
+    const pendingNodes = [rootNode];
+    while (pendingNodes.length) {
+      const currentNode = pendingNodes.pop();
+      if (Array.isArray(currentNode.children)) {
+        // This one will be dropped all together with compiler when we drop Vue.js 2 support
+        modifyKeysInsideTemplateTag(currentNode);
 
-    // However, without `whitespace: condense` Vue will treat spaces between comments
-    // and nodes itself as text nodes, resulting in multi-root component
-    // For multi-root component passing classes / attributes fallthrough will not work
+        dropVOnceForChildrenInsideVIfBecauseOfIssue7725(currentNode);
 
-    // See https://github.com/vuejs/core/issues/7909 for details
+        // See https://github.com/vuejs/core/issues/7909 for details
+        // However, this issue applies not only to root-level nodes
+        // But on any level comments could change slot emptiness detection
+        // so we simply drop them
+        filterCommentNodes(currentNode);
 
-    // To fix that we simply drop all component comments only on top-level
-    rootNode.children = rootNode.children.filter((n) => n.type !== COMMENT_NODE_TYPE);
+        fixSameSlotsInsideTemplateFailingWhenUsingWhitespacePreserveDueToIssue6063(currentNode);
 
-    const pendingNodes = [rootNode];
-    while (pendingNodes.length) {
-      const currentNode = pendingNodes.pop();
-      if (getPropIndex(currentNode, 'for') !== -1) {
-        if (currentNode.tag === 'template') {
-          // This one will be dropped all together with compiler when we drop Vue.js 2 support
-          modifyKeysInsideTemplateTag(currentNode);
-        }
-
-        // This one will be dropped when https://github.com/vuejs/core/issues/7725 will be fixed
-        const vOncePropIndex = getPropIndex(currentNode, 'once');
-        if (vOncePropIndex !== -1) {
-          currentNode.props.splice(vOncePropIndex, 1);
-        }
+        currentNode.children.forEach((child) => pendingNodes.push(child));
       }
-
-      currentNode.children?.forEach((child) => pendingNodes.push(child));
     }
 
     return compilerDomCompile(rootNode, options);
diff --git a/jest.config.base.js b/jest.config.base.js
index 3cbf2fdd61b6a8c20b765134c22e1cc73c39b985..d11b3a5c1e67079d412237ca627b6c4ce7c6e8c5 100644
--- a/jest.config.base.js
+++ b/jest.config.base.js
@@ -51,6 +51,7 @@ module.exports = (path, options = {}) => {
         experimentalCSSCompile: false,
         compiler: require.resolve('./config/vue3migration/compiler'),
         compilerOptions: {
+          whitespace: 'preserve',
           compatConfig: {
             MODE: 2,
           },
diff --git a/spec/frontend/vue3migration/compiler_spec.js b/spec/frontend/vue3migration/compiler_spec.js
new file mode 100644
index 0000000000000000000000000000000000000000..3623f69fe07b2b82eaf744e3cfbc71c11a4a22f1
--- /dev/null
+++ b/spec/frontend/vue3migration/compiler_spec.js
@@ -0,0 +1,38 @@
+import { mount } from '@vue/test-utils';
+
+import SlotsWithSameName from './components/slots_with_same_name.vue';
+import VOnceInsideVIf from './components/v_once_inside_v_if.vue';
+import KeyInsideTemplate from './components/key_inside_template.vue';
+import CommentsOnRootLevel from './components/comments_on_root_level.vue';
+import SlotWithComment from './components/slot_with_comment.vue';
+import DefaultSlotWithComment from './components/default_slot_with_comment.vue';
+
+describe('Vue.js 3 compiler edge cases', () => {
+  it('workarounds issue #6063 when same slot is used with whitespace preserve', () => {
+    expect(() => mount(SlotsWithSameName)).not.toThrow();
+  });
+
+  it('workarounds issue #7725 when v-once is used inside v-if', () => {
+    expect(() => mount(VOnceInsideVIf)).not.toThrow();
+  });
+
+  it('renders vue.js 2 component when key is inside template', () => {
+    const wrapper = mount(KeyInsideTemplate);
+    expect(wrapper.text()).toBe('12345');
+  });
+
+  it('passes attributes to component with trailing comments on root level', () => {
+    const wrapper = mount(CommentsOnRootLevel, { propsData: { 'data-testid': 'test' } });
+    expect(wrapper.html()).toBe('<div data-testid="test"></div>');
+  });
+
+  it('treats empty slots with comments as empty', () => {
+    const wrapper = mount(SlotWithComment);
+    expect(wrapper.html()).toBe('<div>Simple</div>');
+  });
+
+  it('treats empty default slot with comments as empty', () => {
+    const wrapper = mount(DefaultSlotWithComment);
+    expect(wrapper.html()).toBe('<div>Simple</div>');
+  });
+});
diff --git a/spec/frontend/vue3migration/components/comments_on_root_level.vue b/spec/frontend/vue3migration/components/comments_on_root_level.vue
new file mode 100644
index 0000000000000000000000000000000000000000..78222c059d56a325173c9a3a167481529d5876a4
--- /dev/null
+++ b/spec/frontend/vue3migration/components/comments_on_root_level.vue
@@ -0,0 +1,5 @@
+<template>
+  <!-- root level comment -->
+  <div><slot></slot></div>
+  <!-- root level comment -->
+</template>
diff --git a/spec/frontend/vue3migration/components/default_slot_with_comment.vue b/spec/frontend/vue3migration/components/default_slot_with_comment.vue
new file mode 100644
index 0000000000000000000000000000000000000000..d2589104a5da2e9b7bb18e06c0532f1a161f68f4
--- /dev/null
+++ b/spec/frontend/vue3migration/components/default_slot_with_comment.vue
@@ -0,0 +1,18 @@
+<script>
+import Simple from './simple.vue';
+
+export default {
+  components: {
+    Simple,
+  },
+};
+</script>
+<template>
+  <simple>
+    <!-- slot comment typical for gitlab-ui, for example -->
+    <!-- slot comment typical for gitlab-ui, for example -->
+    <slot></slot>
+    <!-- slot comment typical for gitlab-ui, for example -->
+    <!-- slot comment typical for gitlab-ui, for example -->
+  </simple>
+</template>
diff --git a/spec/frontend/vue3migration/components/key_inside_template.vue b/spec/frontend/vue3migration/components/key_inside_template.vue
new file mode 100644
index 0000000000000000000000000000000000000000..af1f46c44e6cb0342b9bf0c5ca69ee0e48301135
--- /dev/null
+++ b/spec/frontend/vue3migration/components/key_inside_template.vue
@@ -0,0 +1,7 @@
+<template>
+  <div>
+    <template v-for="count in 5"
+      ><span :key="count">{{ count }}</span></template
+    >
+  </div>
+</template>
diff --git a/spec/frontend/vue3migration/components/simple.vue b/spec/frontend/vue3migration/components/simple.vue
new file mode 100644
index 0000000000000000000000000000000000000000..1d9854b5b4df8fc0fad02290ab139e1253847d2f
--- /dev/null
+++ b/spec/frontend/vue3migration/components/simple.vue
@@ -0,0 +1,10 @@
+<script>
+export default {
+  name: 'Simple',
+};
+</script>
+<template>
+  <div>
+    <slot>{{ $options.name }}</slot>
+  </div>
+</template>
diff --git a/spec/frontend/vue3migration/components/slot_with_comment.vue b/spec/frontend/vue3migration/components/slot_with_comment.vue
new file mode 100644
index 0000000000000000000000000000000000000000..56bb41e432f434540e22877ee77ec5509058b494
--- /dev/null
+++ b/spec/frontend/vue3migration/components/slot_with_comment.vue
@@ -0,0 +1,20 @@
+<script>
+import Simple from './simple.vue';
+
+export default {
+  components: {
+    Simple,
+  },
+};
+</script>
+<template>
+  <simple>
+    <template #default>
+      <!-- slot comment typical for gitlab-ui, for example -->
+      <!-- slot comment typical for gitlab-ui, for example -->
+      <slot></slot>
+      <!-- slot comment typical for gitlab-ui, for example -->
+      <!-- slot comment typical for gitlab-ui, for example -->
+    </template>
+  </simple>
+</template>
diff --git a/spec/frontend/vue3migration/components/slots_with_same_name.vue b/spec/frontend/vue3migration/components/slots_with_same_name.vue
new file mode 100644
index 0000000000000000000000000000000000000000..37604cd9f6efdc121818aab5ff976abe08967249
--- /dev/null
+++ b/spec/frontend/vue3migration/components/slots_with_same_name.vue
@@ -0,0 +1,14 @@
+<script>
+import Simple from './simple.vue';
+
+export default {
+  name: 'SlotsWithSameName',
+  components: { Simple },
+};
+</script>
+<template>
+  <simple>
+    <template v-if="true" #default>{{ $options.name }}</template>
+    <template v-else #default>{{ $options.name }}</template>
+  </simple>
+</template>
diff --git a/spec/frontend/vue3migration/components/v_once_inside_v_if.vue b/spec/frontend/vue3migration/components/v_once_inside_v_if.vue
new file mode 100644
index 0000000000000000000000000000000000000000..708aa7a96c260e5456b0b061e8099c5591c9f52e
--- /dev/null
+++ b/spec/frontend/vue3migration/components/v_once_inside_v_if.vue
@@ -0,0 +1,12 @@
+<script>
+export default {
+  name: 'VOnceInsideVIf',
+};
+</script>
+<template>
+  <div>
+    <template v-if="true">
+      <div v-once>{{ $options.name }}</div>
+    </template>
+  </div>
+</template>
diff --git a/spec/frontend/vue_compat_test_setup.js b/spec/frontend/vue_compat_test_setup.js
index 8c0346e619836b71d6627d23ec4480f70d7a64e7..6eba9465c802fd4cbc75346290dd0567e1f5f93b 100644
--- a/spec/frontend/vue_compat_test_setup.js
+++ b/spec/frontend/vue_compat_test_setup.js
@@ -63,7 +63,7 @@ if (global.document) {
   };
 
   let compatH;
-  Vue.config.compilerOptions.whitespace = 'condense';
+  Vue.config.compilerOptions.whitespace = 'preserve';
   Vue.createApp({
     compatConfig: {
       MODE: 3,