DEV Community

Jose .
Jose .

Posted on • Originally published at iamjosepunto.Medium

Migrating a Legacy Razor + JavaScript Frontend to React and TypeScript, One Component at a Time

Migrating a Legacy Razor + JavaScript Frontend to React and TypeScript

One Component at a Time

๐ŸŒ Web ยท ๐Ÿ’ผ LinkedIn ยท ๐Ÿ™ GitHub


Most articles about React migrations start from a clean, modern setup. Real enterprise apps rarely look like that. This is the story of migrating a production frontend that had grown for years as a tangle of JavaScript glued into Razor and MVC views โ€” and how we moved it to React and TypeScript without ever stopping to rewrite everything from scratch.

If you maintain an ASP.NET application from the 2010s, this will probably feel familiar.


Index


๐Ÿ‘‹ The starting point: JavaScript tangled into Razor and MVC

The application was a corporate platform for managing measurement equipment and calibrations. On the backend it was solid ASP.NET. The frontend was the problem.

Over the years, the UI had been built the way many .NET apps were built back then: server-rendered Razor views with JavaScript sprinkled directly on top. A bit of jQuery here, an inline script there, DOM manipulation mixed with markup, business logic living half in the view and half in a script block at the bottom of the page.

None of it was "wrong" when it was written. But it added up. The single biggest pain wasn't performance or looks โ€” it was maintainability. The code was tangled. Changing one screen meant tracing logic across Razor, scattered JS files and inline handlers, never quite sure what else you might break. Onboarding anyone new to a screen took far too long. Every change carried risk.

When the cost of touching the frontend becomes higher than the value of the change you're making, you know it's time.


โš™๏ธ Why React and TypeScript

The decision wasn't about chasing a trend. It came down to two concrete needs.

React gave us a real component model. Instead of logic spread across views and scripts, each piece of UI could become a self-contained component with clear inputs and a predictable render. That alone addressed most of the "tangled" problem.

TypeScript gave us types. In a codebase where a lot of bugs came from data being shaped differently than expected โ€” a field that was sometimes a string and sometimes null, an API response that quietly changed โ€” static typing meant catching those mistakes at compile time instead of in production.

Together they targeted exactly what hurt: hard-to-maintain code and avoidable runtime errors.


๐Ÿงฉ The real challenge: making the old and the new coexist

Here is the part most tutorials skip, and the part that actually mattered.

We could not stop the world to rewrite the app. It was in production, in daily use. A full rewrite would have meant months with no new features and a risky big-bang release at the end. So we migrated progressively, component by component โ€” and that meant the legacy Razor/JS frontend and the new React/TypeScript frontend had to live side by side, in the same application, at the same time.

This coexistence was the hardest technical problem of the whole project. A few things we had to work through:

Mounting React inside existing views. Rather than replacing whole pages at once, we mounted React components into specific containers within the existing Razor pages. A page could be mostly server-rendered, with one section already taken over by a modern React component. That let us migrate the riskiest or most-changed parts first, and leave stable parts alone.

Two worlds sharing one page. While both stacks coexisted, we had to be deliberate about boundaries: where the old code ended and the new began, how state was kept from being duplicated across both, and making sure two different paradigms touching the same screen didn't fight over the DOM.

A consistent contract with the backend. The React components talked to the same backend services the old code did. Defining clear, typed contracts for those API responses โ€” so the new TypeScript side knew exactly what it was receiving โ€” was essential to stop the old data-shape bugs from simply moving into the new code.

The guiding principle throughout: every step had to leave the application fully working and shippable. No long-lived migration branch, no "we'll fix it later" state. Migrate a component, verify, ship, repeat.


โœ… What actually improved

The migration delivered on what we set out to fix, and the gains were tangible:

  • Fewer production errors. TypeScript caught entire classes of bugs โ€” mismatched data shapes, missing fields, wrong assumptions about API responses โ€” before they ever reached users.
  • More maintainable, organized code. Self-contained components replaced tangled view-plus-script logic. You could open one component and understand it without tracing five other files.
  • Faster development of new features. Once the component foundation was in place, building something new meant composing existing pieces instead of fighting the old structure.
  • Reusable components. UI that used to be copy-pasted between screens became shared components, written once and reused.

๐Ÿงช Lessons for anyone facing the same migration

If you're staring at a legacy Razor + JavaScript frontend and wondering how to modernize it, a few takeaways from this experience:

You don't need a big-bang rewrite. Progressive, component-by-component migration is slower on paper but dramatically less risky, and it keeps delivering value the whole way.

Plan the coexistence deliberately. The hardest part isn't writing React โ€” it's making the old and new stacks share the same app cleanly while you transition. Decide your boundaries up front.

Let TypeScript do the heavy lifting at the seams. The points where new code meets old data are where bugs hide. Typed contracts with the backend are what turn "it broke in production" into "it didn't compile."

Migrating a real enterprise frontend isn't glamorous, but it's one of the highest-leverage things you can do for a long-lived application: lower risk on every future change, fewer late-night production incidents, and a codebase the team can actually move fast in.


๐Ÿ“ฌ About the author

I'm a Senior Full Stack .NET Developer and Industrial Engineer with 10+ years building web applications, REST APIs and enterprise software with .NET, C#, React, TypeScript, SQL and MongoDB.

๐ŸŒ Web ยท ๐Ÿ’ผ LinkedIn ยท ๐Ÿ™ GitHub

Top comments (0)