diff --git a/app/assets/javascripts/diffs/components/diff_row.vue b/app/assets/javascripts/diffs/components/diff_row.vue
index dfca6d61270805f1eb75fc7ba1b38a8d452e3362..1f5c9b4f2f5675f4ded5ce04bd632371f032f5bc 100644
--- a/app/assets/javascripts/diffs/components/diff_row.vue
+++ b/app/assets/javascripts/diffs/components/diff_row.vue
@@ -6,6 +6,7 @@ https://gitlab.com/gitlab-org/gitlab/-/merge_requests/57842
 * */
 import { memoize } from 'lodash';
 import { isLoggedIn } from '~/lib/utils/common_utils';
+import { compatFunctionalMixin } from '~/lib/utils/vue3compat/compat_functional_mixin';
 import {
   PARALLEL_DIFF_VIEW_TYPE,
   CONFLICT_MARKER_THEIR,
@@ -24,6 +25,10 @@ import * as utils from './diff_row_utils';
 export default {
   DiffGutterAvatars,
   CodeQualityGutterIcon: () => import('ee_component/diffs/components/code_quality_gutter_icon.vue'),
+
+  // Temporary mixin for migration from Vue.js 2 to @vue/compat
+  mixins: [compatFunctionalMixin],
+
   props: {
     fileHash: {
       type: String,
diff --git a/app/assets/javascripts/emoji/components/emoji_group.vue b/app/assets/javascripts/emoji/components/emoji_group.vue
index 4f4c32af113426fd85cd6595b1eefe64d0822159..bbac6866636d9b51e298b6d22ac49fe27d7eb72d 100644
--- a/app/assets/javascripts/emoji/components/emoji_group.vue
+++ b/app/assets/javascripts/emoji/components/emoji_group.vue
@@ -1,5 +1,10 @@
 <script>
+import { compatFunctionalMixin } from '~/lib/utils/vue3compat/compat_functional_mixin';
+
 export default {
+  // Temporary mixin for migration from Vue.js 2 to @vue/compat
+  mixins: [compatFunctionalMixin],
+
   props: {
     emojis: {
       type: Array,
diff --git a/app/assets/javascripts/lib/utils/vue3compat/compat_functional_mixin.js b/app/assets/javascripts/lib/utils/vue3compat/compat_functional_mixin.js
new file mode 100644
index 0000000000000000000000000000000000000000..ec8feb7d2e6b4ac49e573580fef40e986b366235
--- /dev/null
+++ b/app/assets/javascripts/lib/utils/vue3compat/compat_functional_mixin.js
@@ -0,0 +1,14 @@
+import Vue from 'vue';
+
+export const compatFunctionalMixin = Vue.version.startsWith('3')
+  ? {
+      created() {
+        this.props = this.$props;
+        this.listeners = this.$listeners;
+      },
+    }
+  : {
+      created() {
+        throw new Error('This mixin should not be executed in Vue.js 2');
+      },
+    };
diff --git a/app/assets/javascripts/vue_shared/components/form/form_footer_actions.vue b/app/assets/javascripts/vue_shared/components/form/form_footer_actions.vue
index 26c50345c19f0f2fb448af90a1d1de6baa278bf5..fe221d2fefa4b96646e969dc042284197e48ea68 100644
--- a/app/assets/javascripts/vue_shared/components/form/form_footer_actions.vue
+++ b/app/assets/javascripts/vue_shared/components/form/form_footer_actions.vue
@@ -1,5 +1,4 @@
-<!-- eslint-disable-next-line vue/no-deprecated-functional-template -->
-<template functional>
+<template>
   <footer class="form-actions d-flex justify-content-between">
     <div><slot name="prepend"></slot></div>
     <div><slot></slot></div>
diff --git a/ee/app/assets/javascripts/vue_shared/security_reports/components/vulnerability_detail.vue b/ee/app/assets/javascripts/vue_shared/security_reports/components/vulnerability_detail.vue
index 6445cbb99d7652bc32060be3968595fcdf6bdd14..3c8f7a86245687c8cc6dd8852e10f60e82879bef 100644
--- a/ee/app/assets/javascripts/vue_shared/security_reports/components/vulnerability_detail.vue
+++ b/ee/app/assets/javascripts/vue_shared/security_reports/components/vulnerability_detail.vue
@@ -10,10 +10,9 @@ export default {
 };
 </script>
 
-<!-- eslint-disable-next-line vue/no-deprecated-functional-template -->
-<template functional>
+<template>
   <div class="d-sm-flex my-sm-2 my-4">
-    <label class="col-sm-2 text-sm-right gl-font-weight-bold gl-pl-0">{{ props.label }}:</label>
+    <label class="col-sm-2 text-sm-right gl-font-weight-bold gl-pl-0">{{ label }}:</label>
     <div class="col-sm-10 gl-pl-0 gl-text-gray-500">
       <slot></slot>
     </div>
diff --git a/patches/@vue+compiler-sfc+3.2.47.patch b/patches/@vue+compiler-sfc+3.2.47.patch
new file mode 100644
index 0000000000000000000000000000000000000000..9cbedbb0d0ff0f6587dde79f35489f4407cb4b03
--- /dev/null
+++ b/patches/@vue+compiler-sfc+3.2.47.patch
@@ -0,0 +1,28 @@
+diff --git a/node_modules/@vue/compiler-sfc/dist/compiler-sfc.cjs.js b/node_modules/@vue/compiler-sfc/dist/compiler-sfc.cjs.js
+index 6093500..5e9bcbb 100644
+--- a/node_modules/@vue/compiler-sfc/dist/compiler-sfc.cjs.js
++++ b/node_modules/@vue/compiler-sfc/dist/compiler-sfc.cjs.js
+@@ -5226,7 +5226,8 @@ function parse$4(source, { sourceMap = true, filename = DEFAULT_FILENAME, source
+                     const templateBlock = (descriptor.template = createBlock(node, source, false));
+                     templateBlock.ast = node;
+                     // warn against 2.x <template functional>
+-                    if (templateBlock.attrs.functional) {
++                    // disabled by patch-package in gitlab as temporary migration
++                    if (false && templateBlock.attrs.functional) {
+                         const err = new SyntaxError(`<template functional> is no longer supported in Vue 3, since ` +
+                             `functional components no longer have significant performance ` +
+                             `difference from stateful ones. Just use a normal <template> ` +
+diff --git a/node_modules/@vue/compiler-sfc/dist/compiler-sfc.esm-browser.js b/node_modules/@vue/compiler-sfc/dist/compiler-sfc.esm-browser.js
+index 2ce8d7a..2fbe75b 100644
+--- a/node_modules/@vue/compiler-sfc/dist/compiler-sfc.esm-browser.js
++++ b/node_modules/@vue/compiler-sfc/dist/compiler-sfc.esm-browser.js
+@@ -37376,7 +37376,8 @@ function parse$5(source, { sourceMap = true, filename = DEFAULT_FILENAME, source
+                     const templateBlock = (descriptor.template = createBlock(node, source, false));
+                     templateBlock.ast = node;
+                     // warn against 2.x <template functional>
+-                    if (templateBlock.attrs.functional) {
++                    // disabled by patch-package in gitlab as temporary migration
++                    if (false && templateBlock.attrs.functional) {
+                         const err = new SyntaxError(`<template functional> is no longer supported in Vue 3, since ` +
+                             `functional components no longer have significant performance ` +
+                             `difference from stateful ones. Just use a normal <template> ` +