Blazor and .NET

Migrating Web Forms to Blazor: screen by screen, without a big-bang rewrite

How to move an ASP.NET Web Forms application to Blazor and .NET 10 screen by screen behind a proxy, what moves easily, what is hard, and how to start.

Vlado Pandžić

Vlado Pandžić · Founder · Senior .NET architect
Published · 6 min read

Plenty of companies still run an important application on ASP.NET Web Forms: an order portal, an internal system for the warehouse or the back office, something that has worked for fifteen years. It still works. But every change takes longer, the developers who know it are leaving, and nobody wants to be the one who touches it.

The good news: moving to Blazor does not have to mean a year of rewriting while the business waits. The application can be moved screen by screen, while users keep working and barely notice.

Why Web Forms cannot simply be upgraded

Web Forms does not exist in modern .NET. It was never ported to ASP.NET Core, so there is no “upgrade to .NET 10” button for it. The pages have to be rebuilt.

.NET Framework 4.8 is still supported for as long as the Windows version it runs on, so nothing switches off tomorrow. But it gets no new features, new libraries increasingly skip it, and developers who want to work in Web Forms are getting harder to find every year.

Two ways: everything at once, or screen by screen

Everything at once looks simpler: a new application is built next to the old one and switched over on one day. In practice it means months without new features in the old application, two applications to maintain in parallel, and a go-live where everything has to work at the same moment.

Screen by screen turns this around. A proxy is placed in front of the old application, and every screen that moves to Blazor is served by the new application from then on. Users keep the same address and the same login, and move from old screens to new ones without noticing.

  1. 1Inventory of every screen
  2. 2Proxy in front of the old application
  3. 3Screen by screen to Blazor
  4. 4The old application is switched off

The proxy: how old and new work together

The proxy is YARP, Microsoft’s reverse proxy, running inside the new Blazor application. Everything the new application knows how to serve, it serves itself. Everything else is forwarded to the old Web Forms application.

builder.Services.AddReverseProxy()
    .LoadFromConfig(builder.Configuration.GetSection("ReverseProxy"));

app.MapRazorComponents<App>()
    .AddInteractiveServerRenderMode();

app.MapReverseProxy();

In the configuration, one route catches all addresses, with a low priority, so it only applies when no Blazor page matches:

"ReverseProxy": {
  "Routes": {
    "webforms": { "ClusterId": "webforms", "Order": 1000, "Match": { "Path": "{**catch-all}" } }
  },
  "Clusters": {
    "webforms": { "Destinations": { "old": { "Address": "https://orders-legacy.internal/" } } }
  }
}

When the orders screen is rebuilt in Blazor, the new application starts serving /orders, and the old screen is simply no longer reached. Login and session can be shared between the two applications with Microsoft’s System.Web adapters, so users do not have to sign in twice.

What one screen looks like, before and after

A typical Web Forms grid with paging:

<asp:GridView ID="OrdersGrid" runat="server" AutoGenerateColumns="false"
    AllowPaging="true" PageSize="20" OnPageIndexChanging="OrdersGrid_PageIndexChanging">
    <Columns>
        <asp:BoundField DataField="Number" HeaderText="Order" />
        <asp:BoundField DataField="Customer" HeaderText="Customer" />
        <asp:BoundField DataField="Total" HeaderText="Total" DataFormatString="{0:C}" />
    </Columns>
</asp:GridView>

The same screen in Blazor, with the QuickGrid component that ships with .NET:

@inject OrderService Orders

<QuickGrid Items="orders" Pagination="pagination">
    <PropertyColumn Property="@(o => o.Number)" Title="Order" />
    <PropertyColumn Property="@(o => o.Customer)" Title="Customer" />
    <PropertyColumn Property="@(o => o.Total)" Title="Total" Format="C" />
</QuickGrid>

<Paginator State="pagination" />

@code {
    private readonly PaginationState pagination = new() { ItemsPerPage = 20 };
    private IQueryable<Order> orders = Enumerable.Empty<Order>().AsQueryable();

    protected override async Task OnInitializedAsync()
        => orders = (await Orders.GetOpenAsync()).AsQueryable();
}

The markup looks similar. The real difference is what is behind it: no ViewState, no postback for every page change, and OrderService is ordinary C# that can be tested and reused.

What moves easily, and what does not

In Web Forms In Blazor Effort
Business logic in separate classes The same classes, as services Small
Business logic inside code-behind Moved out into services first Medium
Master pages Layouts Small
User controls (.ascx) Components Small to medium
GridView, Repeater, FormView QuickGrid or a component library Medium
Pages built around ViewState and postbacks Component state, with the flow rethought Medium
Third-party Web Forms controls Their Blazor versions, or other components Medium to large
Crystal Reports Another reporting tool Large
Login and session shared between old and new System.Web adapters or a shared cookie Medium, done once at the start

The biggest variable is not Blazor, but how much of the logic is buried in code-behind. Where the logic is already in separate classes, a screen often moves in days. Where every button click holds a hundred lines of SQL and UI code mixed together, that logic has to be pulled out first.

In which order to move the screens

  1. First, one simple screen, to prove that the proxy, the login and the deployment work end to end.
  2. Then the screens that hurt the most: the ones that change often, are slow, or that users complain about. That is where the move pays off first.
  3. Rarely used administrative screens last. The inventory often finds screens nobody uses any more, and those do not need to move at all.

A checklist before you start

  • A list of every page, with a note on which ones are actually used (logs or analytics).
  • NuGet packages and third-party controls. Our free .NET Framework migration tool checks which of them have a version for .NET 10.
  • How login works today: Forms authentication, Windows authentication or ASP.NET Identity.
  • Where the business logic lives: in separate classes or in code-behind.
  • Reports, exports and emails the application sends.
  • Who tests each screen when it moves, and who signs it off.

For the wider picture of moving from .NET Framework, see the article on migrating from .NET Framework to .NET 10, and for the question of whether Blazor is a safe choice at all, Is Blazor still relevant in 2026?.

How we work

This is exactly what we do: we move Web Forms applications to Blazor screen by screen, behind a proxy, while users keep working in the application. We start with an inventory and a plan showing which screens move first and where the hard parts are. More on the approach is on the Blazor page, and the first step is a free 30-minute call.

Sources

This article is general information only, not legal, tax, financial or other professional advice. Scenarios, examples and calculations are illustrative. Terms of use and disclaimer.

Related articles

© 2026 ProCoding — All rights reserved.Legal notice and privacyTerms of useSplit, Croatia