I've had this post waiting for a while but due to a bug in Preview 5 I've been holding off sharing it. But with the release of Preview 6 the bug is gone and I can finally hit the publish button. So let's get to it!

One of the many awesome things about Blazor is the migration story it offers. If you're currently developing or maintaining a MVC or Razor Pages application, then you're in for a treat! You can now replace parts of your application with Blazor components.

In this post, we're going to cover how to make all the necessary changes to your existing app to allow Blazor components to be used. Then we'll also look at replacing part of an existing MVC view with a Blazor component.

All of the source code from this blog post is available on GitHub.

Getting Setup

We're going to be using the Contoso University sample application. This should make things a little more interesting than using the default MVC templates.

As Blazor is only available in .NET Core 3, I've taken the current .NET Core 2.2 version and upgraded it using this guide on the Microsoft Docs site.

Adding Blazor Support to an existing MVC application

In order to use Blazor in an existing MVC or Razor Pages application we need to make a few changes, they are.

  • Add a reference to blazor.server.js
  • Add the Blazor services to the service container (DI)
  • Map Blazors SignalR hub

Referencing Blazors JS

First on our list is to add a reference to blazor.server.js, we need to do this in the main _Layout.cshtml file.

<footer class="border-top footer text-muted">
    <div class="container">
        &copy; 2019 - Contoso University - <a asp-area="" asp-controller="Home" asp-action="Privacy">Privacy</a>
    </div>
</footer>

<script src="_framework/blazor.server.js"></script>

<environment include="Development">
    <script src="~/lib/jquery/dist/jquery.js"></script>
    <script src="~/lib/bootstrap/dist/js/bootstrap.bundle.js"></script>
</environment>

Adding Blazors services

Next, we need to add the services Blazor requires to the service container. We do this in the Startup.cs file.

public void ConfigureServices(IServiceCollection services)
{
    ...

    services.AddServerSideBlazor();
    services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_3_0);
}

Mapping Blazors SignalR hub

Last but not least, we need to hook up Blazors SignalR hub to the endpoint routing, again in Startup.cs.

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
    ...

    app.UseEndpoints(endpoints =>
    {
        endpoints.MapControllerRoute("default", "{controller=Home}/{action=Index}/{id?}");
        endpoints.MapBlazorHub();
    });
}

With those 3 things in place our application is now ready to use Blazor components.

Adding a Blazor component

With our application primed for using Blazor, we are going to replace the grid used on the Courses Index view with a Blazor component. The Index view currently looks like this.

@model IEnumerable<ContosoUniversity.Models.Course>

@{
    ViewData["Title"] = "Courses";
}

<h2>Courses</h2>

<p>
    <a asp-action="Create">Create New</a>
</p>
<table class="table">
    <thead>
        <tr>
            <th>
                @Html.DisplayNameFor(model => model.CourseID)
            </th>
            <th>
                @Html.DisplayNameFor(model => model.Title)
            </th>
            <th>
                @Html.DisplayNameFor(model => model.Credits)
            </th>
            <th>
                @Html.DisplayNameFor(model => model.Department)
            </th>
            <th></th>
        </tr>
    </thead>
    <tbody>
        @foreach (var item in Model)
        {
            <tr>
                <td>
                    @Html.DisplayFor(modelItem => item.CourseID)
                </td>
                <td>
                    @Html.DisplayFor(modelItem => item.Title)
                </td>
                <td>
                    @Html.DisplayFor(modelItem => item.Credits)
                </td>
                <td>
                    @Html.DisplayFor(modelItem => item.Department.Name)
                </td>
                <td>
                    <a asp-action="Edit" asp-route-id="@item.CourseID">Edit</a> |
                    <a asp-action="Details" asp-route-id="@item.CourseID">Details</a> |
                    <a asp-action="Delete" asp-route-id="@item.CourseID">Delete</a>
                </td>
            </tr>
        }
    </tbody>
</table>

Creating the CoursesList component

We're going to start by creating a new folder in the route called Components, this is my preference but you can call this folder whatever you want. In fact you could put your components anywhere you want to in the project.

Then we're going to add a new component called CoursesList.razor with the following code.

@using Microsoft.AspNetCore.Components
@using ContosoUniversity.Models

<table class="table">
    <thead>
        <tr>
            <th>
                Number
            </th>
            <th>
                Title
            </th>
            <th>
                Credits
            </th>
            <th>
                Department
            </th>
            <th></th>
        </tr>
    </thead>
    <tbody>
        @foreach (var item in Courses)
        {
            <tr>
                <td>
                    @item.CourseID
                </td>
                <td>
                    @item.Title
                </td>
                <td>
                    @item.Credits
                </td>
                <td>
                    @item.Department.Name
                </td>
                <td>
                    <a href="Courses/Edit/@item.CourseID">Edit</a> |
                    <a href="Courses/Details/@item.CourseID">Details</a> |
                    <a href="Courses/Delete/@item.CourseID">Delete</a>
                </td>
            </tr>
        }
    </tbody>
</table>

@code {
    [Parameter] public IEnumerable<Course> Courses { get; set; }
}

As you can see the code is based on the table from the original Index view. We've declared the list of courses as a Parameter to be passed in. Then we're just iterating over the courses and displaying each one as a row in the table, mimicking the behaviour of the original Index view.

Using the CoursesList component in a view

To use our new CoursesList component, we're going to update the original Index view to look like this.

@model IEnumerable<ContosoUniversity.Models.Course>

@{
    ViewData["Title"] = "Courses";
}

<h2>Courses</h2>
    
<p>
    <a asp-action="Create">Create New</a>
</p>

@(await Html.RenderComponentAsync<CoursesList>(RenderMode.ServerPrerendered, new { Courses = Model }))

We've removed the original table and replaced it with a call to the RenderComponentAsync HTML helper. This helper is responsible for adding and wiring up our component correctly.

It's worth noting that this way of adding components is not going to be the long term solution. The plan is to be able to add components using normal elements, so in our case the above code would look like this.

@model IEnumerable<ContosoUniversity.Models.Course>

@{
    ViewData["Title"] = "Courses";
}

<h2>Courses</h2>
    
<p>
    <a asp-action="Create">Create New</a>
</p>
    
<CoursesList Courses="Model" />

This work is being tracked by the following issue on GitHub, #6348.

And, that's it! We've now successfully replaced a section of a MVC view with a Blazor component. You can fire up the app and browse to the page and everything should look exactly the same as before.

Summary

The ability to replace parts of a MVC view or a Razor page with Blazor components is incredibly powerful. And it offers a great migration path for anyone who is looking to modernise an existing application.

In this post, I showed how you can enable an existing MVC application to use Blazor components. Then showed how to replace part of an existing MVC view with a Blazor component.