Telerik blogs

Are you exploring how to modernize existing applications, moving from MVC, Razor Pages or Web Forms? Take a look at what Blazor brings to the table.

Blazor is evolving. With .NET 8 just around the corner, there’s a lot of talk about Blazor becoming a “drop-in” replacement for MVC or Razor Pages.

But if you’re looking to modernize existing applications and move from building with MVC, Razor Pages or even Web Forms, what does Blazor bring to the table?

It All Comes Down to HTML and CSS in the End

Let’s start with one really important truth: websites are (still) built on two key pillars—HTML and CSS.

Over the years there have been various attempts to create abstractions on top of these core technologies.

Web Forms and Silverlight required the use of bespoke UI markup languages. MVC, Razor Pages and Blazor keep you “closer to the metal” by employing Razor syntax.

The Razor markup language looks a lot like HTML, albeit with some extra ASP.NET specific syntax thrown in for good measure. It enables you to mix C# and HTML together in a way that .NET can process, and turn into HTML to be rendered in the browser.

Here’s a simple “hello world” in Razor Pages:

Hello World (Razor Pages)

@page
@model WelcomeModel
<h2>Welcome to Razor Pages</h2>
<p>Your username is: @Model.Username</p>
public class WelcomeModel : PageModel
{
    public string Username { get; set; } = "User";

    public void OnGet()
    {
        // You can do any additional processing here before displaying the page
    }
}

Here’s the same example using components in a Blazor app:

Hello World (Blazor)

@page "/welcome"
<h2>Welcome to Blazor</h2>
<p>Your username is: @Username</p>
@code {
    
    public string Username { get; set; } = "User";
    
}

Blazor components are self-contained and can be used like pages (as above, where requests to /welcome will be routed to this component) or components that can be embedded in other pages.

In both cases, your Razor markup will be rendered as HTML in the end, and you’re never far away from writing plain old HTML yourself when building your apps.

This brings advantages (compared to building the equivalent UI using something like Web Forms or XAML). It means you’re learning and using HTML/CSS, which gives you transferrable knowledge and skills.

The HTML and CSS itself is transferrable between projects and frameworks. Before now, I’ve taken views created with React and copy/pasted them to a Blazor app with minimal changes needed (to the markup) to keep the UI looking the same in the browser.

Plus it’s easy to get help in HTML and CSS because they’re so ubiquitous.

Maintainable Web Apps, Built Faster Using Components

Of course, if Razor was the only benefit, there’d be no particular reason to opt for Blazor over MVC or Razor Pages.

The biggest difference between Razor Pages/MVC and Blazor is the component model. Blazor is built on a powerful component model that enables you to break your UI down into smaller components, then compose those components together to form larger features.

For example, take this product list page:

A page showing popular products. Each product has an image placeholder, price and description in a sort of card layout. There are three products listed horizontally across the page.

We could build that in a single Blazor component using Razor syntax:

ProductList.razor

@page "/products"
@inject IProductStore Store

<section class="container">
    <div class="row">
        <h2>Popular Products</h2>
        @foreach (var product in products)
        {
            <div class="col-sm-4 mb-4">
                <div class="card">
                    <img class="card-img-top" src="/images/@product.Image" alt="Product Image Description"/>
                    <div class="card-body">
                        <h5 class="card-title">@product.Title</h5>
                        <p class="card-text">@product.Price.ToString("C")</p>
                    </div>
                    <div class="card-footer">
                        <p class="text-center">FREE Delivery</p>
                    </div>
                </div>
            </div>
        }
    </div>
</section>
@code {
    private List<ProductSummary> products;

    protected override Task OnInitializedAsync()
    {
        products = Store.List();
        return base.OnInitializedAsync();
    }
}

This fetches a list of products, assigns them to a field called products. In the markup we iterate over the product list and render standard HTML for each one.

Now let’s be honest, we all know how this goes. The feature starts off nice and simple, but it doesn’t take many iterations for the UI and logic to spiral out of control! It can be helpful, even at this early stage, to think about splitting this UI into separate components.

Blazor makes this straightforward, we can take part of the UI and move it into a separate component:

ProductCard.razor

<div class="card">
    <img class="card-img-top" src="/images/@Product.Image" alt="Product Image Description"/>
    <div class="card-body">
        <h5 class="card-title">@Product.Title</h5>
        <p class="card-text">@Product.Price.ToString("C")</p>
    </div>
    <div class="card-footer">
        <p class="text-center">FREE Delivery</p>
    </div>
</div>

Modern web development is all about components, dividing features into smaller, reusable pieces like this, each encapsulating a small part of the user interface (including its behavior).

Now we can use the new component as though it were a standard HTML element, in our product list page:

ProductList.razor

...

<section class="container">
    <div class="row">
        <h2>Popular Products</h2>
        @foreach (var product in products)
        {
            <div class="col-sm-4 mb-4">
                <ProductCard Product="product"></ProductCard>
            </div>
        }
    </div>
</section>

With this, we’ve encapsulated the UI for product summary cards in one handy component.

We can modify and extend this component (for example, having variations for sponsored or featured products) without making changes to anything else. Plus, if the new component starts to ramp up in complexity again we can break it down even further.

Reuse Components (Where It Makes Sense)

This component-based approach really comes into its own when you need to reuse the same piece of UI in multiple places.

Say, for example, you’re using a form with a standard submit button in your app:

Index.razor

<!-- other markup -->

<button type="submit" class="btn btn-primary">Submit</button>

Now you decide you want all your submit buttons to look (and behave) the same.

You can take that markup and put it into a dedicated component:

SubmitButton.razor

<button type="submit" class="btn btn-primary">@ChildContent</button>
@code {

    [Parameter]
    public RenderFragment ChildContent { get; set; }

}

Then use it in place of the original button:

Index.razor

<!-- other markup -->

<SubmitButton>Submit</SubmitButton>

SubmitButton uses a handy Blazor concept called Render Fragments.

These enable you to accept arbitrary content as a parameter to your component, then render it in a place of your choosing.

Here it means we can accept content declared between the opening <SubmitButton> and closing </SubmitButton> tags and render it (using @ChildContent) as the content for the underlying HTML button.

Now we’ve a handy SubmitButton component which provides a consistent look and feel throughout the app. If we make changes to SubmitButton, those changes will be reflected in every part of the app which uses this component.

With a little bit of effort to extract components as you go you’ll soon find yourself able to spin up new features much faster because each part of your app is neatly encapsulated in small (easy to understand) components, plus you can turn to your library of reusable building blocks for common UI and functionality.

Interactive—When You Need It to be

So far we’ve talked about how components offer a productive way to build your web applications. But what about running them?

This is where Blazor brings several options (and more to come in .NET 8).

If you consider your traditional web app:

  • The browser sends a request for a page to your server
  • Your ASP.NET web app handles the request (routes it to a controller/Razor Page)
  • ASP.NET renders the view/page and generates HTML
  • The resulting HTML is returned to the browser

If you want users to be able to interact with your app you have to use forms (aka PostBacks, hello Web Forms developers!) to take user input, re-render the page on the server, and return the new/updated HTML back to the browser.

But what about those times you need your components to run more interactively, like a calculator where users can use sliders to adjust input parameters and see the results near instantaneously?

This is hard to do with forms alone, and would typically have you reaching for JavaScript.

But with Blazor you don’t need to turn to JS, because you can run your components interactively via Blazor Web Assembly or Blazor Server.

Take this slider calculator:

Calculator.razor

@page "/calculator"
@attribute [RenderModeServer]

<h3>Slider Calculation</h3>

<div class="form-group">
    <label>Slider 1:</label>
    <input type="range" min="1" max="100" @bind="slider1Value" @bind:event="oninput" />
    <p>Current value: @slider1Value</p>
</div>

<div class="form-group">
    <label>Slider 2:</label>
    <input type="range" min="1" max="100" @bind="slider2Value" @bind:event="oninput"/>
    <p>Current value: @slider2Value</p>
</div>

<div class="form-group">
    <label>Total:</label>
    <p>@total</p>
</div>
@code {
    int slider1Value = 1; 
    int slider2Value = 1;

    int total => slider1Value + slider2Value;
}

It enables users to use two sliders to adjust the input numbers and see the resulting calculation in real time.

A basic web page showing two sliders (slider 1 and slider 2) and the combined total of both sliders

Because Blazor runs interactively (in either of its hosting modes) this component will “just work,” no JavaScript needed.

But what if you don’t want the initial download and loading delays that accompany Blazor Web Assembly, or the latency of handling all DOM interactions via the server that you get with Blazor Server?

Keep an Eye on .NET 8 and Server-Side Rendering

Today, with .NET 7 (or lower) you have to choose whether you want your entire app to run via Web Assembly or Blazor Server.

But .NET 8 will make it possible to use Blazor as a drop-in replacement for Razor Pages or MVC via server-side rendering and forms support.

A diagram showing a client and server. The client sends a request for the home page to the server, which renders Blazor components then returns plain HTML to the client

You’ll be able to handle requests to a page (say /product/) by routing it to the relevant Blazor component (like our ProductList component), have it render on the server and return HTML to the browser.

This way you get to leverage all the advantages of building with components, plus the simplicity (and performance) of server-side rendering.

Then, if you want specific components (like the calculator) to run interactively, you can do so via an attribute:

Calculator.razor

@page "/calculator"
@attribute [RenderModeServer]

...

Or the new rendermode parameter when declaring instances of the component.

Index.razor

<Calculator @rendermode="@RenderMode.Server" />

In both cases, this Calculator component will be rendered on the server first (so the initial HTML can be returned to the browser). Then Blazor Server will kick in to make the component fully interactive.

This way you can have interactive components without forcing the entire app to run over a socket connection (as it would in .NET 7 and below, if you were using Blazor Server).

This mixed-mode rendering for Blazor looks set to become a popular way to build .NET web apps in the coming months and should make switching to Blazor from MVC, Razor Pages or Web Forms a lot simpler.

Migrating from Web Forms/MVC or Razor Pages to Blazor

Components are a key pillar of building modern web apps, and can help you build more maintainable web applications, faster.

But what about your existing apps—how do you set about migrating those?

The details will vary, based on the specific way your apps are implemented, but here’s the process I would use:

  • Migrate one view/page at a time.
  • Make the new view reference existing services to fetch/update data.
  • Refactor to components, but only when you’ve got something working.

Moving view markup from Razor Pages/MVC to Blazor is fairly straightforward as you’re using Razor in both cases. Your UI is likely dependent on data coming from somewhere.

In Blazor you can inject services into your components, use them to fetch data, then bind your UI to that data:

@page "/users"
@inject IUserService UserService

<table class="table table-striped">
    <thead>
    <tr>
        <th>Name</th>
        <th>Role</th>
        <th>Last Login</th>
        <th>Logins (last 30 days)</th>
    </tr>
    </thead>
    <tbody>
    @foreach (var user in Model.Users)
    {
        <tr>
            <td>@user.Name</td>
            <td>@user.Role</td>
            <td>@user.LastLogin</td>
            <td>@user.RecentLoginCount</td>
        </tr>
    }
    </tbody>
</table>
@code {

    private UserListModel Model;

    protected override async Task OnInitializedAsync()
    {
        Model = await UserService.ListUsers();
    }

}

This assumes we had an IUserService to start with.

The trick is to take one view/page at a time, and reuse existing services if you have them (that way you’re effectively changing the View part of your UI, but none of the underlying logic).

If you find you don’t have clearly defined services, or your existing Views/Pages have a lot of logic in them, you can either try to move that logic to the new Blazor component or push it down into the service then use that.

It’s worth avoiding the temptation to refactor more than strictly necessary until you have something that works in the new Blazor app.

For example, you might start seeing opportunities to split your page down into smaller components. I’d avoid the temptation to do this before you’ve got something working, or you’ll wind up trying to refactor broken code!

It’s typically easier to migrate a view in its entirety first, then refactor it once you have something that works.

In Summary

Blazor brings a productive component-based, modern approach to ASP.NET web development.

When migrating existing features, take it one piece at a time, reuse existing backend logic/services where possible, and pick your moments to refactor.

Check out this article on how to migrate an MVC application to Blazor Server or WASM for more specific ideas and tips. Alternatively, here’s a handy approach for migrating from Web Forms to Blazor.

Finally, .NET 8 looks set to make migration from MVC or Razor Pages a lot easier, with the option to use Razor components as drop-in replacements for your existing pages and views, so it’s also worth keeping an eye on that ahead of its November 2023 release.

Try Telerik UI for Blazor


Develop new Blazor apps and modernize legacy web projects in half the time with 100+ truly native, easy-to-customize Blazor components to cover any requirement. Progress Telerik is committed to keeping pace with Microsoft’s release cadence, so you’ll always be up to speed. Try it for free with our 30-day trial and enjoy our industry-leading support.


Try Now


Jon Hilton
About the Author

Jon Hilton

Jon spends his days building applications using Microsoft technologies (plus, whisper it quietly, a little bit of JavaScript) and his spare time helping developers level up their skills and knowledge via his blog, courses and books. He's especially passionate about enabling developers to build better web applications by mastering the tools available to them. Follow him on Twitter here.

Related Posts

Comments

Comments are disabled in preview mode.