How We Scaled A Legacy App With jQuery To Vue 3 Migration
Last Updated on June 18, 2026
Quick Summary
This insight walks through a jQuery to Vue 3 migration that Bacancy delivered for a US logistics SaaS company whose shipment-tracking dashboard had outgrown its original jQuery codebase. It covers what was failing, why Vue 3 was the right target, the factors assessed during discovery, the phased strategy used during execution, the performance and scalability gains measured 60 days after cutover, and the lessons that apply to any team still running jQuery.
Table of Contents
Introduction
In late 2025, a US-based logistics SaaS company approached us for a jQuery to Vue 3 migration on the shipment-tracking and order-management dashboard that sits at the heart of their product. The platform had been in production since 2015. It followed the common pattern of its era: server-rendered pages with jQuery handling everything interactive, supported by a long list of plugins for tables, dropdowns, modals, and date pickers. Roughly 800 business customers signed in every day, and close to 25,000 warehouse and operations staff relied on it to move freight.
The application worked, and that was never in doubt. The real issue was that it had stopped being able to grow. jQuery is far from obsolete, and the numbers confirm it. The 2025 Stack Overflow Developer Survey shows jQuery still ranked the third most-used web framework, ahead of several newer options. Plenty of production systems run on it without trouble; the difference here was scale. A small website using a bit of jQuery is manageable, but a 120,000-line dashboard attempting to support real-time tracking on top of a decade’s worth of DOM-heavy code is a different challenge altogether, and this client had reached that ceiling.
Three problems kept recurring: large shipment tables had begun to freeze the browser, each new feature meant copying event-handler logic that already existed elsewhere, and bringing a new developer up to speed was taking more than a month, because nothing in the codebase explained what anything did. After a year of working around all three, a jQuery to Vue 3 move was no longer something the team could keep postponing. This insight breaks down how the migration was planned and executed while the product stayed live.
The Breaking Point: Where Our jQuery Codebase Started Holding Us Back
The most visible symptom was the main shipment table. On high-volume accounts, it rendered more than 5,000 rows straight into the DOM in one pass. With no virtualization in place, the browser painted every row whether or not it was on screen. On the rugged tablets used across the warehouse floor, opening that view could lock the tab for three to four seconds. Operations staff had learned to filter heavily just to make the screen usable, which undercut the whole purpose of having the data in front of them.
A deeper problem sat beneath that one: state lived in the DOM. The current filter, the selected shipments, the active sort order, all of it was read back out of HTML attributes and element classes whenever the code needed a value. That arrangement holds up until two features read the same element and interpret it differently. Bugs caused by stale DOM reads had become a weekly event.
Duplication made everything heavier. The logic for opening a shipment-detail modal existed, in slightly different versions, across more than 40 pages. A fix applied in one place corrected exactly one page. The team had also accumulated two versions of the same table plugin on different screens, because removing the older one safely would have required testing every page that touched it.
All of this carried a weight cost. The JavaScript bundle had grown to 4.8 MB uncompressed. There was no tree-shaking and no code splitting, so every user downloaded the entire payload on first load. For a tool people open dozens of times a day, that delay adds up. None of these failures was truly jQuery’s fault. They were what tends to happen when an application keeps growing while the layer underneath it holds no opinion about structure.
Why We Chose Vue 3 Over Other Modern Frameworks
The client raised the obvious question early. If a modernization was happening anyway, why Vue 3 rather than React, which is more widely adopted, or Svelte, which looks faster on paper?
Incremental adoption decided it. A full rewrite of a live revenue system was off the table, which meant the new framework had to mount into specific parts of the existing server-rendered pages and run with jQuery throughout the transition. Vue 3 handles that cleanly. You can create a small Vue application, embed Vue.js inside a jQuery page by attaching it to a single container, and let the two operate together without conflict. React supports progressive embedding as well, but it tends to steer teams toward an all-or-nothing single-page model sooner, and it brings more upfront tooling decisions. The fuller picture of where each one fits is covered in our Vue vs React comparison.
Vue 3’s reactivity model answered the second half of the question. The stale-DOM bugs this client kept hitting all trace back to treating the DOM as the source of truth. Vue inverts that: change the data, and the view updates on its own. That single shift removed an entire category of defects before a line of new feature code existed.
The team mattered too. The client’s eleven developers came from a jQuery and server-template background. Vue’s single-file components map almost directly onto the model they already understood, with the template at the top, the logic below, and scoped styles at the bottom. That familiarity shortened the learning curve in a way the timeline depended on. Svelte is a capable framework, and its compile-time approach produces lean output. Its smaller ecosystem and hiring pool, however, made it a harder commitment for a long-lived enterprise platform the client would need to staff for years. Vue 3 was the lowest-risk route to the outcome the client wanted.
Planning a jQuery to Vue.js Move and Need Engineers Who Have Done it on Live Systems?
Hire Vue.js developers from Bacancy to plan and execute your migration with a structured, low-risk approach.
Key Factors We Evaluated Before the jQuery to Vue 3 Migration
No Vue code was written until we had spent two weeks on discovery. A migration plan built on guesses about a ten-year-old codebase is a plan that slips, so we wanted an accurate picture first.
Codebase Size and jQuery Plugin Sprawl
The frontend ran to roughly 120,000 lines of JavaScript, most of it jQuery. The plugin count told the more important story: more than 60 jQuery plugins, including two separate table libraries, three date pickers, and an assortment of UI widgets added by different developers over the years. Each replacement was effectively its own small project, so cataloguing them upfront shaped how we sequenced the entire effort.
Build Tooling and Bundle Strategy
The existing pipeline concatenated scripts through an aging Gulp setup, with no tree-shaking, no code splitting, and no module boundaries. A modern build had to be part of the work, and we standardized on Vite for fast development feedback and proper bundle splitting. That choice alone would later account for a large share of the payload reduction.
Existing Test Coverage
This was the riskiest figure we found. Backend coverage sat near 40 percent, while frontend coverage was close to zero. Refactoring a system you cannot verify is a gamble, so before touching the high-traffic dashboard views, we wrote characterization tests around the most critical flows: shipment creation, status updates, and the filter logic. Those tests locked in the current behavior, so later changes stayed reversible.
Global State and DOM Coupling
We mapped where the state actually lived. Dozens of window-level variables and DOM-stored values served as an informal global state. Untangling them mattered because running jQuery with Vue during coexistence needed one agreed source of truth rather than two systems reading the same DOM and reaching different conclusions.
Team Readiness on Vue 3
Of the eleven developers, four had used Vue before, and none had worked with the Composition API in production. Before execution began, we ran a two-day internal workshop on the exact patterns the project would rely on, covering reactivity, single-file components, and Pinia for state, so the team could build with shared conventions from the first day.
Strategy We Used in jQuery to Vue 3 Migration
We ran the jQuery to Vue 3 migration as a phased strangler approach, replacing the old system one piece at a time while it stayed fully live. Execution took 16 weeks on top of the 2-week discovery phase.
Step 1: Mount Vue 3 Alongside jQuery
We introduced Vite and configured Vue 3 to mount into named container elements inside the existing pages. The jQuery application kept running exactly as it had, and Vue took ownership of a container only when that piece was migrated. Running jQuery with Vue this way meant every existing feature still functioned while the new code grew around it. Standing the two side by side took about a week.
Step 2: Build a Shared State Layer
Before migrating any real feature, we spent a week setting up Pinia as the single source of truth and wrapping the backend calls in a typed fetch layer. That gave both the new Vue components and the remaining jQuery code one consistent place to read and write shared data during the transition, which is what kept the two systems from drifting out of sync.
Step 3: Migrate Low-Risk Components First
We started with standalone widgets that carried no dependencies on the rest of the page, including filter chips, toast notifications, confirmation modals, and form fields. These were safe wins that let the team practice the new patterns on real production code without exposing core workflows. Over roughly three weeks, the team built fluency on parts of the app where a mistake cost very little.
Step 4: Replace the Heavy jQuery Plugins
This phase was the core of the cleanup, and it ran about four weeks. We consolidated the two table plugins into a single virtualized Vue table component, replaced the three date pickers with one Vue date picker, and moved the dropdown and select widgets to Vue equivalents. Reducing each job to one component is what later allowed us to delete thousands of lines of code.
Step 5: Rebuild the Core Dashboard
With the foundation in place, we rebuilt the main dashboard views in Vue 3 and reimplemented the shipment table with row virtualization, so only the visible rows render no matter how many exist, and the freezing disappeared. Because these views carried the most traffic, each one went out behind a feature flag and passed through a short canary period before full rollout. The Vue Composition API did the heavy lifting across these five weeks, letting us group logic by feature and turn the modal and filter code that jQuery had forced us to repeat into reusable composables.
Step 6: Remove jQuery and Lock In Standards
The final two weeks went to removing jQuery and its plugins from the bundle entirely and consolidating everything onto the Vite build. To stop the codebase from regressing, we added CI guardrails: an ESLint rule that blocks any new jQuery import, a bundle-size budget that fails the build when the payload crosses a set threshold, and updated review guidelines covering Vue and Pinia conventions.
What Changed After Migration: Performance and Scalability Gains in Vue 3
The complete migration was delivered in 18 weeks. The figures below are the outcomes recorded during the first 60 days after the final cutover.
Metric
Before Migration
60 Days After
JavaScript bundle (uncompressed)
4.8 MB
1.6 MB
Dashboard time to interactive
6.2 seconds
2.4 seconds
5,000-row shipment table render
3.5s, frequent freezing
~400ms, no freezing
Lighthouse performance score
38
84
Average PR review time
52 minutes
33 minutes
New developer onboarding to first PR
4 to 5 weeks
~2 weeks
Frontend production bugs per month
31
9
Dashboard features delivered per quarter
2
5
The render time and time-to-interactive numbers were the gains users felt right away. Warehouse staff stopped over-filtering to force the page to open, because it opened on its own. The bundle dropping to roughly a third of its former size mattered most on the older floor tablets, where every megabyte of JavaScript had been costing real seconds.
The quieter gains were arguably the larger ones. Cutting monthly frontend bugs from 31 to 9 came almost entirely from removing the stale-DOM class of errors. Once data lived in Pinia rather than being read back out of HTML, an entire failure mode simply ceased to exist. The rise from two to five dashboard features per quarter was not a matter of the team working harder either. It came from a codebase that no longer resisted change. That shift, from a frontend that resisted every change to one the team could extend within days, is the scalability outcome our Vue.js migration services are built to produce. It was most evident in the work that had been delayed the longest. The real-time tracking module the client had wanted for two years was the feature that set this engagement in motion. Built natively in Vue 3 after the migration, it went live without a single jQuery-related issue.
Lessons for Teams Still Running jQuery Applications
This project surfaced a set of practical lessons that apply broadly to teams working with large jQuery codebases, irrespective of the framework they ultimately adopt.
The Framework Is Rarely the Real Problem
jQuery was not failing this client. The absence of structure around it was. State was scattered across the DOM, logic was duplicated across dozens of pages, and there were no module boundaries. Any framework can collect that debt over time. Before blaming the tool, look at whether the genuine issue is that nothing in the codebase enforces structure. That diagnosis changes what you choose to migrate toward.
Coexistence Is Safer Than a Rewrite
The pull toward starting fresh is strong, and it is almost always the riskier call for a system already serving customers. Pairing jQuery with Vue side by side let us migrate one piece at a time while the product stayed fully usable throughout. No one had to commit to a single launch date and absorb the risk of a hard cutover.
Test Coverage Has to Come First
The near-zero frontend coverage was the single biggest risk discovery surfaced. You cannot safely refactor what you cannot verify. Writing characterization tests around the critical flows first felt slow in the moment, and it is what made every later step reversible instead of a wager.
Build Fluency on Low-Risk Components
Migrating leaf components such as filters and modals before touching core workflows gave the team real practice with the new patterns on production code, without exposing the high-traffic views while everyone was still learning.
Guardrails Keep the Gains in Place
Migrations quietly slide backward when a deadline arrives and someone reaches for an old habit. The ESLint rule blocking new jQuery imports and the bundle-size budget in CI are unglamorous, and they are the reason the cleanup held.
Conclusion
A jQuery to Vue 3 migration is seldom about the framework being old. It is about an application reaching a scale where jQuery’s lack of structure costs more than it returns, measured in bugs, in onboarding time, and in features that never get built because the codebase resists them. For this client, an incremental and production-safe jQuery to Vue.js transition preserved a decade of proven business logic while removing the ceiling it had been pressing against. The numbers held 60 days later, and more importantly, the team could finally build what their roadmap had been promising.
Every legacy frontend is different, and the right path depends on your codebase size, your test maturity, and how much risk a live cutover carries for your users. That is where the partner you choose makes the difference. As a Vue.js development company that has modernized production systems while keeping them live, Bacancy can assess your existing jQuery application, map the safest path to Vue 3, and put a team on it that has done this work before.
It depends on the size of the codebase, the number of plugins involved, and the level of test coverage at the start. A small application can be done in a few weeks. A large production system like the 120,000-line dashboard in this case study took 18 weeks, including a 2-week discovery phase, because it was migrated incrementally while staying live.
Yes, and for most live applications, that is the safer choice. Vue 3 can mount into specific parts of an existing page and run alongside jQuery, which lets you migrate piece by piece. This strategic approach keeps the product fully functional throughout instead of risking everything on one large cutover. For the framework-level mechanics of moving to Vue 3, our Vue 2 to Vue 3 migration tutorial covers the setup, createApp, and Composition API changes step by step.
Vue 3 supports incremental adoption especially well, which matters when you are modernizing a live system rather than starting fresh. Its single-file components also map closely to the template-based model that teams coming from jQuery already know, which shortens the learning curve. React is a strong option too, though it tends to push toward a full single-page model sooner.
The most common issues are stale-state bugs from logic that treated the DOM as the source of truth, conflicts between jQuery plugins and their Vue replacements, and gaps in test coverage that make refactoring risky. Mapping global state and adding tests around critical flows before starting prevents most of them.
It can, significantly, though the gains come from how the application is rebuilt rather than from Vue alone. In this project, virtualizing a large table, introducing a modern build with code splitting, and removing redundant plugins cut the bundle to about a third of its previous size and brought the time to interactive down from 6.2 to 2.4 seconds.