back to blog
    Feb 2026·12 min

    Why I Migrated a 200K LOC Angular App to React — And What I Learned

    A deep dive into the strategy, tooling, and hard lessons from migrating a massive enterprise application between frameworks.

    AngularReactMigration

    There was never a day when we stopped shipping features to migrate. Two hundred thousand lines moved route by route over fourteen months, with both frameworks live in production the entire time.

    Strangler pattern at the route level

    The shell stayed Angular until the last quarter. Migrated routes mounted a React root inside an Angular component — a boring bridge that never broke.

    ts
    @Component({ selector: "app-react-bridge", template: "<div #host></div>" })
    export class ReactBridgeComponent implements OnChanges, OnDestroy {
      @Input() component!: ComponentType<any>;
      @Input() props: Record<string, unknown> = {};
      @ViewChild("host", { static: true }) host!: ElementRef<HTMLDivElement>;
    
      private root?: Root;
    
      ngOnChanges() {
        this.root ??= createRoot(this.host.nativeElement);
        this.root.render(createElement(this.component, this.props));
      }
    
      ngOnDestroy() {
        this.root?.unmount();
      }
    }

    Extract logic before you touch UI

    The real asset was fifteen years of business rules buried in services. We pulled them into framework-free modules first, consumed them from both sides, and only then rewrote templates.

    ts
    // domain/pricing.ts — no Angular, no React, fully unit-testable
    export function priceOrder(order: Order, rules: PricingRules): Money {
      const base = order.lines.reduce((sum, l) => sum + l.qty * l.unitCents, 0);
      const discount = rules.tiers.find((t) => base >= t.minCents)?.percent ?? 0;
      return { cents: Math.round(base * (1 - discount / 100)), currency: order.currency };
    }

    What I would do differently

    • Freeze the design system first. We migrated components while their styles were still moving, and paid for it twice.
    • Budget for two build pipelines. Bundle size regressed before it improved.
    • Write the shared domain layer on day one, not month four.
    • Measure per-route Core Web Vitals from the start so 'React is faster' is a claim with evidence.

    The migration was worth it — hiring, DX, and ecosystem all improved. But the durable win was the framework-free domain layer, which would have paid off even if we had stayed.