Draft: [POC] Remove Vue 2

What does this MR do and why?

Proof of concept. Do not merge.

This MR builds GitLab with no Vue 2 at all. It answers one question: what breaks when Vue 2 is gone? #634491 tracks the real removal.

The full pipeline passes, including E2E on Omnibus and CNG and the as-if-FOSS pipeline: https://gitlab.com/gitlab-org/gitlab/-/pipelines/2972081727. It ran on the stack rebased on 2026-10-21.

The MR is stacked on !263251 (merged), which compiles templates with the Vue 3 compiler in the Vue 3 CI jobs. That MR is itself stacked on the spec fixes in !263821 (merged). Most fixes for the Vue 3 compiler are already in those MRs or on master.

The commits on top of it form the task list for the real removal. Some tasks work while Vue 2 is still installed, so they can merge first, as separate MRs. The others only make sense once Vue 2 is gone.

Tasks that can be done before Vue 2 is removed

Each of these works with Vue 2 and Vue 3 side by side.

  • Update @gitlab/ui to 138.7.0, with the router-link fixes in GlNavItem and GlLink. Merged in !263855 (merged).
  • Find an empty catalog input description in QA when it is hidden. Merged in !263634 (merged).
  • Register DisabledByPolicyPopover and PolicyBadge in the CE branch_rule.vue. The Vue 3 compiler resolves every component in a template, even one behind a false v-if. Merged in !263806 (merged).
  • Build Storybook with Vue 3. !263808 (merged) does this. Storybook has its own package.json, so it can switch before the app does:
    • Use @storybook/vue3-webpack5 and vue-loader 17.
    • Give the preset's loader the GitLab compiler options.
    • Drop the preset's vue$ alias, so stories use @vue/compat like the app.
    • Bind the story args in the global decorator, because the Vue 3 renderer passes no props to a story.
  • Move chalk to dependencies. scripts/frontend/postinstall.js needs it, and production installs get it only through vue-loader 15.
  • Read templates with the Vue 3 compiler in the gettext extractor. The plain compiler crashes on notes/components/diff_with_note.vue, so the extractor uses the compiler wrapper and compat options of the bundler.
  • Finish the rollout: mark every vue3_migration.yml entry as migrated. The 373 entries with status: rollout then build once, under their own name.
  • After the rollout, delete the 100 unused vue3_migrate_* feature flags, and expect no rollout entries in the Vue 3 migration spec.
  • Remove the Omnibus vue3-rollout E2E job and qa/.../vue3_rollout_spec.rb. The job enables vue3_migrate_jobs, and that flag goes with the step above. Not done in this POC yet.

Tasks that must be done when or after Vue 2 is removed

  • Build and test with Vue 3 by default. VUE_VERSION and VUE_COMPILER_VERSION default to 3 in webpack, rspack, Vite and Jest. Vite no longer imports the Vue 2 plugin or compiler.
  • Remove Vue 2 and its packages:
    • vue changes to 3.5.43
    • vue-template-compiler
    • vue-loader 15
    • @vitejs/plugin-vue2
    • @vue/vue2-jest
    • the Vue 2 versions of vuex, vue-router, vue-apollo, portal-vue and vue-virtual-scroll-list
  • Add Vue Test Utils 2 under its own package name, and let the Jest Vue mock re-export the aliased vue.
  • Keep the aliased packages external in the ESLint import-x/order rule. The resolver maps vue, vuex and the others to files in the repo, so a pathGroups entry is needed.
  • Drop the -vue3 suffix from the assets image tag in scripts/utils.sh. The suffix kept the Vue 2 and Vue 3 images apart. With Vue 3 as the default, Omnibus waited for an image that was never pushed.
  • Remove the duplicate vue3 CI jobs, such as compile-production-assets vue3. They now build exactly what the default jobs build. Not done in this POC yet.
  • Remove the migration code: config/vue3migration/, the infection scanner, lib/gitlab/vue3_migration.rb and the ?vue3 imports. Not done in this POC.
  • Replace the Vue 2 libraries that still work through the aliases. Not done in this POC:
    • @tiptap/vue-2
    • @testing-library/vue 5
    • vuedraggable 2
    • vue-resize and vue-observe-visibility, which the vendored vue-virtual-scroller imports
    • portal-vue 2, a dependency of @gitlab/ui and @gitlab/duo-ui
  • Fix the Failed to resolve component: router-link warning from GlNavItem in the super sidebar. See the screenshots section.

Labels

pipeline:run-all-e2e runs the full E2E suite against the Omnibus and CNG packages. pipeline::tier-3 runs the full pipeline.

Screenshots or screen recordings

Checked in GDK with the rspack dev server, which logged Using Vue.js 3 (compiler 3) and built with no errors. Signed in as root:

Page Apps mounted under Vue 3 Console errors
Project jobs list top bar, sidebar, breadcrumbs, JobsTableAppRoot none from Vue
Merge request changes tab 20 apps, including the MR header, sidebar widgets and file browser none from Vue
Work item WorkItemsRoot, DuoPanel. The rich text comment editor (tiptap) accepts input, and **text** turns bold. none
Pipeline PipelineHeaderApp, PipelineDetailsRoot. The graph shows 100 jobs. none

The only error on these pages is a 401 from the performance bar, which is not related to Vue.

One development warning repeats on every page: Failed to resolve component: router-link, from GlNavItem in the super sidebar. The Vue 3 compiler resolves router-link before it picks the v-if branch, and these pages install no router. The links render as plain <a> elements. It is a cleanup item, not a breakage.

How to set up and validate locally

  1. Check out this branch and run yarn install.
  2. Restart the GDK frontend server and Rails.
  3. Open a project's jobs page.
  4. In the browser console, run document.querySelector('[data-gitlab-vue3-app]'). It returns the root element of the jobs app, which mounted under Vue 3.

MR acceptance checklist

Evaluate this MR against the MR acceptance checklist. It helps you analyze changes to reduce risks in quality, performance, reliability, security, and maintainability.

References

Edited by Miguel Rincon

Merge request reports

Loading
Loading