Migrating a jQuery-heavy app to a component framework
A jQuery application is made of pages, plugins and scripts that share the DOM, with no components to convert. This guide explains how to move it to Angular in small steps while it keeps running.
Why jQuery apps are a different problem
jQuery is a library for working with the DOM. It is still maintained. The difficulty comes from the shape of applications built on it over many years:
- State lives in the DOM. The current value of something is whatever a hidden field or a CSS class says.
- Scripts are organized per page. Each server-rendered page includes its own files, and they run in the order of the script tags.
- Handlers are global. Code binds to
documentor to broad selectors, so one script can affect another page region without anyone noticing. - Plugins do the heavy lifting. Grids, date pickers, modals and validation each come from a plugin with its own conventions.
- The server returns HTML. AJAX calls often fetch rendered fragments and insert them into the page.
There is no component tree to translate. The migration has to create that structure.
Decide the end state first
Not every jQuery application should become a single-page application.
| End state | Description | Fits when |
|---|---|---|
| Server pages with component islands | The server keeps rendering pages. Interactive areas are Angular components | Content-heavy sites, few complex screens |
| SPA for the application area | The complex, interactive part becomes an Angular app. Simple pages stay on the server | Back-office tools with a public site around them |
| Full SPA | The server exposes only an API | Highly interactive products |
Choosing the smallest end state that solves your problem saves a lot of work.
Step 1: Inventory pages and plugins
List every page with its scripts, its plugins and its AJAX calls. Rank pages by usage and by how often they change. A page that is used daily and changed often gains the most from components. A page that nobody has edited in years can wait or stay as it is.
For each plugin, record whether it is still maintained and where it is used. The library migration index helps with common replacements.
Step 2: Turn HTML endpoints into data endpoints
Components need data. For each area you plan to migrate, add JSON endpoints next to the existing ones that return HTML. Keep the old endpoints until the old pages are gone. This backend work is often a large hidden item in a jQuery migration, so size it during the assessment.
Step 3: Set up a build and a shared layer
- A modern build that produces bundles the existing pages can include.
- A shared stylesheet or design tokens, so new components match the old pages.
- One service for the session. With cookie authentication on the same site this usually works without changes, but handle expiry in one place.
Step 4: Start with islands
Angular components can be packaged as custom elements. The server-rendered page includes the bundle and uses a tag such as <order-summary> where the old markup was. The page around it stays untouched.
Each island replaces one region and its jQuery code. Release it behind a switch, as described in feature flags in an incremental migration.
Step 5: Move from islands to pages
When most of a page consists of components, replace the whole page with an Angular route. The server or the proxy sends that URL to the Angular application. This is the edge-level seam of the strangler fig pattern. Repeat page by page for the area that should become an SPA.
Step 6: Remove jQuery where it is no longer needed
When a page has no jQuery code left, drop the script includes for it. At the end, remove the plugins and, if nothing else depends on it, the library.
Pitfalls
- Global handlers touching the new components. An old script that binds to every
inputon the page will also bind to inputs inside an island. Narrow the old selectors or use Shadow DOM for the island. - Global CSS. Old stylesheets with broad rules leak into new components. Scope the new styles and review resets.
- Wrapping plugins forever. A jQuery plugin wrapped inside a component is a temporary measure. Give each wrapper a replacement date.
- Two sources of truth. While old scripts and new components share a page, agree which one owns each piece of data. Communicate through events or attributes. Avoid reading each other's DOM.
When the server side is part of the problem
jQuery code is usually tied to the server templates that produce its markup. If the pages come from ASP.NET MVC or from JSP, the untangling steps are similar to those in the guides for AngularJS with ASP.NET MVC and AngularJS with Java and JSP.
More in this section
- Migrating Backbone, Knockout and old Ember applications
- Migrating AngularJS that lives inside ASP.NET MVC
- Migrating AngularJS that lives inside Java and JSP
- How to keep teams shipping during a migration
Planning a migration or a micro-frontend platform?
In a 20-minute call you describe your situation and I tell you how I'd approach it. If it isn't worth doing yet, I'll say so.
Message me on LinkedIn