Home · About · Services · Industries · Projects · Products · Process · Blog · FAQ · Contact
AngularJS to Angular migration: how to plan it in 2026 AngularJS lost support at the end of 2021. How we choose between a hybrid upgrade, a route-by-route split and a rewrite, and how we size and sequence the work.
An internal portal still runs on AngularJS 1.x. It works, the staff who use it daily know it well, and nobody wants to touch it. Then a security review flags the framework as unsupported, a browser update breaks a date picker, and the one developer who understood the build has moved on. That is usually when the migration conversation starts.
The short answer: an AngularJS to Angular migration starts with an inventory of the app, then a choice between three paths. Run AngularJS and Angular side by side with ngUpgrade and move components one at a time, split the app by route so each section is rebuilt and released separately, or rewrite it in one go if it is small. Most business apps suit one of the first two, released in small steps over several months.
Why this cannot wait much longer Google ended long-term support for AngularJS on 31 December 2021. The last release is 1.8.3, published in April 2022, and the repository is now archived. Vulnerabilities have been disclosed since then that will not be fixed upstream, and the third-party modules most AngularJS apps depend on, such as UI Bootstrap and older grid libraries, stopped receiving updates even earlier. Commercial extended-support builds can buy time, but do not change the direction of travel.
The practical risks are less about a dramatic breach and more about slow decay. It gets harder to hire people who want to work on the code. Build tools such as Bower and old Gulp plugins stop installing on current Node versions. Each small change takes longer than the last. A planned migration is cheaper than an emergency one.
Start with an inventory, not an estimate Before anyone quotes a timeline, we read the code and count what is there. A rough inventory takes a day or two and changes the conversation from guesswork to a list of work items.
Controllers, directives, components, services, factories and filters, with a rough size for each. Routes and how they are defined: ngRoute or UI-Router, and how deeply states are nested. Third-party AngularJS modules, and whether each has an Angular equivalent or must be replaced. Code that relies on $scope inheritance, $rootScope events, $watch or manual $apply calls. The build: script tags, Bower, Grunt, Gulp, Webpack or a mix. Existing tests, and which user journeys have no automated coverage at all. The backend contract: which APIs the frontend calls, and whether they are documented. A few grep commands give a first count and highlights the patterns that make migration harder.
# Rough size of the AngularJS surface, by type
grep -rEo '\.(controller|directive|component|service|factory|filter)\(' src/ \
| sed -E 's/.*\.([a-z]+)\($/\1/' | sort | uniq -c
# Files using patterns that need rework before or during migration
grep -rlE '\$rootScope\.\$(broadcast|emit|on)\(' src/ | wc -l
grep -rlE '\$scope\.\$watch|\$apply\(' src/ | wc -l
grep -rl 'compile:' src/ | wc -lDirectives with compile functions, heavy event broadcasting and deep scope inheritance are the expensive items. Simple .component() definitions with bindings and a controllerAs template map almost one to one onto Angular components.
Choosing the migration path The choice depends on the size of the app, how it is structured, and whether the business can accept a period where two frameworks run at once.
Hybrid with ngUpgrade: both frameworks run in the same page. Angular components can be used inside AngularJS templates and the reverse. Best for large apps with shared layouts and services, where releasing in small steps matters. Route-by-route split: the new Angular app owns some URLs and the legacy app owns the rest, with a reverse proxy routing between them. Simpler than a hybrid, and suits apps whose sections are fairly independent, such as an admin area and a reporting area. Rewrite: build the new app separately and switch over once. Reasonable only for small apps with a handful of screens, or when the existing code is so tangled that a hybrid would spend most of its effort on workarounds. We lean towards the route split when the sections allow it, because each Angular piece is a normal, modern app from day one with no hybrid change detection to reason about. The cost is that shared state, such as the logged-in session and navigation, must work across both apps. Cookie-based sessions on the same domain and a shared header make this manageable. With NGINX in front, the routing itself is a few lines. Menu links in the legacy app simply point at the new paths as each section moves.
# Angular app built with: ng build --base-href /app/
# It owns /app/reports and /app/admin; everything else stays on AngularJS
location /app/ {
alias /var/www/angular-app/browser/;
try_files $uri $uri/ /app/index.html;
}
location / {
root /var/www/legacy-app;
try_files $uri $uri/ /index.html;
}When the sections share a lot of components and services, a hybrid is usually less total work than duplicating them across two apps.
Preparing the AngularJS side first Whichever path you pick, some clean-up inside the AngularJS app pays for itself. These changes are low risk, can ship to production on their own, and make every later step smaller.
Upgrade to AngularJS 1.8.3 if you are on an older 1.x release, and fix what breaks. Move the build to npm and a modern bundler, so AngularJS code is imported rather than loaded with script tags. Convert directives to .component() with explicit bindings, and controllers to controllerAs syntax. Replace $rootScope events with services that hold state and expose methods. Introduce TypeScript file by file. AngularJS code can be typed with @types/angular. Add end-to-end tests with Playwright for the journeys people use every day, run against the legacy app before anything changes. The end-to-end tests are the most important item. They describe what the app does from the user's side, so they keep passing as the implementation underneath changes. Unit tests written for AngularJS controllers rarely survive the move, so we do not invest in porting them. New tests for the Angular code use the runner the current Angular CLI sets up (Vitest in recent versions), since Karma is deprecated.
Running a hybrid with ngUpgrade If you choose the hybrid, the Angular app bootstraps first and then starts AngularJS inside it. The @angular/upgrade/static package is still published with each Angular release. The hybrid needs an NgModule at its root, bootstrapped with bootstrapModule, even if the rest of your Angular code uses standalone components.
// app.module.ts
import { DoBootstrap, NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { UpgradeModule } from '@angular/upgrade/static';
@NgModule({ imports: [BrowserModule, UpgradeModule] })
export class AppModule implements DoBootstrap {
constructor(private upgrade: UpgradeModule) {}
ngDoBootstrap() {
this.upgrade.bootstrap(document.body, ['legacyApp'], { strictDi: true });
}
}
// main.ts
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
platformBrowserDynamic().bootstrapModule(AppModule);
// legacy-registrations.ts: expose an Angular component and service to AngularJS
import { downgradeComponent, downgradeInjectable } from '@angular/upgrade/static';
angular.module('legacyApp')
.directive('invoiceList', downgradeComponent({ component: InvoiceListComponent }))
.factory('invoiceApi', downgradeInjectable(InvoiceApiService));We migrate from the leaves upwards. Small presentational components and shared services go first, then whole screens, then the routing and the outer shell last. If the app uses UI-Router, the @uirouter/angular-hybrid package lets AngularJS and Angular states live in one router while you move screens across. Check that its current release supports your Angular version before you commit to it.
Two cautions. The hybrid relies on Zone.js to keep both frameworks' change detection in step. Recent Angular CLI versions create zoneless projects by default, so install zone.js, list it in the polyfills, and leave zoneless Angular until AngularJS is gone. And every value crossing the boundary has a small cost, so avoid passing large objects back and forth in tight loops or long lists.
Sequencing, releases and estimates We plan the work as a series of releases, each of which leaves the app working in production. A typical order is: preparation and tests, then the build switch, then shared services, then screens in order of business value or pain, and finally removal of AngularJS. We work in two-week increments on a staging environment, so stakeholders see each screen before it ships.
Feature work does not have to stop, but it needs a rule. New screens are built in Angular only. Changes to a legacy screen that is due to move soon are kept small, or the screen is moved first. Without that rule the migration target keeps growing. We have run this pattern ourselves: on one consumer web application we built new features in Angular 13 while the existing AngularJS app kept running underneath.
For estimates, we size each item from the inventory rather than quoting a single number for the whole app. The replacement of third-party modules is often the largest unknown, especially custom grids and complex form widgets, so we prototype those early.
A planning checklist Inventory components, services, routes, third-party modules and risky patterns. Agree the path: hybrid, route split or rewrite, and write down why. Cover daily user journeys with end-to-end tests before changing anything. Modernise the AngularJS build and code style in small, shippable steps. Prototype replacements for third-party widgets early. Move from the leaves upwards, and release after every increment. Set a rule that new features are built in Angular only. Plan the final removal of AngularJS, Zone.js-dependent workarounds and the hybrid bootstrap. If the inventory shows heavy compile-function directives, very little test coverage and no one left who knows the business rules, bring in help before you start rather than halfway through. The planning phase is where most of the risk can be removed cheaply.
Infigini IT Services · 11 Kanwali Road, Dehradun 248001, India · info@infigini.com · +91 95684 87789