An Introduction to Blazor and WebAssembly
What is Blazor?
Blazor is a new framework that lets you build interactive web UIs using C# instead of JavaScript. It is an excellent option for .NET developers looking to take their skills into web development without learning an entirely new language. Currently, there are two ways to work with Blazor: running on an ASP.NET Core server with a thin-client, or completely on the client’s web browser using WebAssembly instead of JavaScript.
In this article, we introduce Blazor and WebAssembly, learn about the creation of Blazor for .NET developers, then build a sample Blazor application and component.
Many .NET developers share a common dream that, until recently, seemed to be just out of reach: the ability to run .NET apps anywhere! The seed of this dream was planted in 2001 when the .NET Framework was less than a year old and an open source project named Mono was announced. The Ximian project team released Mono in 2004 and carried the dream to the Linux platform. A few years later the dream expanded when Silverlight empowered developers to run C# and VB. NET in Internet Explorer with the help of a plugin. The dream gained momentum, reaching into other browsers and even, briefly, touching mobile.
The release of .NET Core in 2016 brought the dream closer to reality and extended the reach of .NET to embrace Windows, Linux, and MacOS. More recently, developers have been able to run .NET on desktops, laptops, tablets, phones, games, sensors, and the cloud.
But one frontier consistently stood in the way of the dream to run .NET everywhere: the browser. ActiveX and plug-ins have been swept aside and developers were left with a single choice to write code that runs in all modern browsers: JavaScript.
Is JavaScript truly the only way? The answer is: No!
A New Hope: WebAssembly
Just a few years ago, an alternative to JavaScript was released to production and implemented in the latest version of major browsers, including Chrome, Edge, Firefox, and Opera. This alternative is a stack-based virtual machine that runs code consistently and without recompilation between 32-bit and 64-bit platforms, across operating systems, inside the major browser brands, and is even supported on mobile devices.
WebAssembly, or Wasm for short, is a sandbox that executes bytecode instructions. The instructions are predefined, strongly typed, and encompass a suite of operations that transcend the limitations of JavaScript. The result is that Wasm is a viable compiler target for a variety of languages and runtimes.
Shortly after WebAssembly was released, the Mono project that began in 2001 was successfully ported to run in the browser, bringing .NET code with it. The dream was almost realized.
Why try to run .NET in the browser in the first place?
- .NET supports modern languages like C#.
- A large .NET ecosystem of solutions already exists.
- .NET has been continuously tuned for improved performance.
- There’s an existing suite of powerful tools for building .NET solutions.
- The framework is stable and mature.
.NET in the browser by itself doesn’t complete the picture, however. WebAssembly can’t interact with the browser’s Document Object Model (DOM) directly to render HTML and must use JavaScript as “glue.” And .NET doesn’t contain a rendering engine for HTML out of the box.
To bridge these gaps, Microsoft Principal Software Engineer Steve Sanderson created Blazor. The dream is realized!
Using Blazor in Full Stack Development
Blazor is a single page application (SPA) framework that runs on .NET in the browser. It sits on top of .NET. The full stack is illustrated in Figure 1.
Blazor provides several services to bridge the gap between .NET and the browser, including:
- Assembly trimming to reduce payloads sent to the browser
- A reusable component model
- A debugging experience
- Dependency injection
- Forms and validation
- IntelliSense and tooling
- JavaScript interoperability
- Layouts
- Routing services
- Server-side rendering
- Static file publishing
- Unit testing
The name “Blazor” is derived from “browser” and “Razor.” Razor refers to the templating engine used by Blazor to define and render the user interface. Although Blazor is new, Razor is not. In fact, Razor is a view engine that’s been used by ASP. NET MVC developers since its first release in 2011. Moreover, ASP. NET Core and Razor class libraries make it possible to share the same components between a traditional ASP. NET MVC application and Blazor! Read the full Blazor documentation online at Microsoft Docs.
The Razor’s Edge
Razor defines views as a combination of Razor markup syntax, HTML, and C#. View components and pages are typically defined with the .cshtml extension, while reusable Razor components have a .razor extension. Razor files contain HTML.
Within the code files, the @ symbol indicates the transition from HTML to C# code. The C# is evaluated and the result is inserted into the template. Razor can evaluate simple statements like this one:
<h1>@DateTime.Now</h1>
This renders something that looks like:
It can also evaluate more complex statements including expressions and loops. The following template contains syntax and code to initialize an array.
<ul>
@foreach(int num in Fibonacci)
{
<li>@num * 2 = @(num*2)</li>
}
</ul>
@code {
public int[] Fibonacci = new int[] { 1, 1, 2, 3, 5, 8 };
}
The result looks like this:
The Blazor architecture should be very familiar to ASP. NET MVC developers. In addition to being able to share Razor components, Blazor follows a similar life cycle and supports dependency injection and middleware. The Startup class contains the same methods an MVC developer would expect, including a ConfigureServices function:
public void ConfigureServices(IServiceCollection services)
{
services.AddRazorPages();
services.AddServerSideBlazor();
services.AddSingleton<WeatherForecastService>();
}
…and a Configure method:
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler("/Error");
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseEndpoints(endpoints =>
{
endpoints.MapBlazorHub();
endpoints.MapFallbackToPage("/_Host");
});
}
The previous example refers to a “ServerSideBlazor” implementation. That requires some further clarification.
Server-side and Client-side Blazor
Blazor was introduced in 2017 and quickly gained momentum. Released as an “experimental project,” its future was uncertain until it was officially adopted by the ASP. NET team. After Blazor was pulled into ASP. NET Core, it was quickly determined that a lot of work was required before client-side (Blazor WebAssembly) would be ready for production.
Work is still needed for performance optimization, assembly trimming, the debug experience, and more. Therefore, client-side Blazor was and is released as a preview feature. It’s available only in preview versions of Visual Studio 2019 (or in the non-preview version if you set the “enable preview” option in settings).
Fortunately, the architecture of Blazor separates the Blazor logic from the actual rendering engine. Because it’s a .NET Standard library that can be referenced and executed on the client as well as the server, the team made a decision to create a server-side version. Learn more about Blazor hosting models at Microsoft Docs.
The server-side version uses the same conventions and Razor templates and has the same support as the client-side. The difference is that the code runs entirely on the server, rather than in WebAssembly on the client.
When server-side Blazor is launched, a lightweight JavaScript client is sent to the browser that establishes real-time communications with the server. The server processes the logic, including rendering, then transmits the results to the client. The result is a lightweight or “thin client” that relies on the server for work. This version of Blazor is production-ready and ships as part of ASP. NET Core 3.0.
What’s important to note is that because it uses the Blazor engine, in most cases server-side code will run “as is” on the client with a simple switch to configuration settings. Aside from the difference between “production-ready” and “preview,” there are a few pros and cons to consider when looking at the two versions.
Blazor WebAssembly | Server-side Blazor |
Thick client. Workload is distributed to each user. | Thin client. Server is responsible for resources. |
Limited to WebAssembly mono implementation that supports the majority of .NET Standard 2.0 APIs. | Support for full runtime. |
DLLs are loaded and executed in the browser. Intellectual property is exposed to the user. | All code is executed and secure on the server. Only UI updates are exchanged with the client. |
Full support for progressive web applications (PWA). Can be installed and run offline. | No offline mode. Requires an active network connection for the client to function. |
Composed of static assets. Can be hosted as a static website and served from a content delivery network (CDN). | Requires an active server that supports ASP. NET to function. |
Majority of logic is performed on the client. Most network requests are low latency. | Relies heavily on server-side communication for rendering and may degrade in low-latency conditions. |
Based on the pros and cons, many developers focused on client-side Blazor are using an implementation that features components shared between a WebAssembly and server-side project. The server-side is used for production and debugging. The client-side will be delivered to production and used to replace the server-side when the client exits preview.
Native Blazor UI Controls vs. Wrapped JavaScript
UI component libraries were created long before Blazor. Most existing frameworks that target web applications are based on JavaScript. They are still compatible with Blazor due to its ability to interoperate with JavaScript. Components that are primarily based on JavaScript are called wrapped JavaScript controls, as opposed to components written entirely in Blazor, which are referred to as native Blazor controls.
Native Blazor controls parse the Razor syntax to generate a render tree that represents the UI and behavior of that control. The render tree is why it’s possible to run server-side Blazor. The tree is parsed and used to generate HTML on the server that’s sent to the client for rendering. In the case of Blazor WebAssembly, the render tree is parsed and rendered entirely in the client. Either way, Blazor maintains internal references to the elements that make up the control and handles refreshing the UI by re-rendering when necessary.
Wrapped JavaScript controls have some caveats that are important to note:
- Wrapped controls must take care to trigger UI refreshes by calling into JavaScript when necessary.
- Referencing elements often requires traversing the DOM as there’s no internal Blazor render tree.
- Support for nested and dynamic controls may be limited.
- JavaScript isn’t directly bound to the Blazor forms and validation system, so either custom validation or a bridge between Blazor and JavaScript must be built.
On the flipside, the benefits of wrapped controls include:
- Full support for existing components based on HTML, CSS, and JavaScript.
- Better performance in many cases due to native JavaScript rendering.
- Improved maintenance as updates may be as simple as updating the control library rather than having to update a reference or package and recompile the app.
For more information about wrapped JavaScript controls, read How to Use a JavaScript Interop Inside a Blazor Component.
Getting Started with Blazor WebAssembly in Visual Studio
Blazor has very mature tooling and support despite its young age. The latest stable version of Visual Studio 2019 provides full server-side Blazor support out of the box. This includes the ability to create projects and components, use IntelliSense, build, debug, publish, and deploy.
Preview support for Blazor WebAssembly in Visual Studio is available by going into Settings → Environment → Preview features and checking “Use previews of the .NET Core SDK.” Most major component vendors also provide support for Blazor.
For example, GrapeCity provides both native and wrapped JavaScript Blazor controls. The difference is that the wrapped controls are more complete since the wrapping process is quicker, and the company was able to take advantage of an entire suite of controls with years of development behind them. Wijmo, the full suite of GrapeCity’s JavaScript UI controls are available via WijmoBlazor. The suite of native controls is smaller but it’s likely to expand rapidly over the next couple years. You can try the Beta today by downloading the ComponentOne Blazor Edition.
You can also work with Blazor projects using the cross-platform, open-source editor Visual Studio Code. Opening a Blazor project will prompt you to install the necessary support for IntelliSense and for building, debugging, and publishing your Blazor app. You can create and run a Blazor app from the .NET Core command-line interface (CLI) with just three lines of script. From your source directory, type:
dotnet new blazorserver -o MyFirstBlazorApp
cd MyFirstBlazorApp
dotnet run
The built-in sample app uses the same example as the ASP. NET MVC templates for easy comparison.
Your First Blazor App
Let's build an actual app to demonstrate what Blazor helps you accomplish.
For this example, you should have Visual Studio 2019 16.3 or later installed with the web development workflow. Create a new project and choose “Blazor App.” Give it any name and specify the location for the code then click Create. Keep all of the default values. The dialog should look similar to this:
Wait for the project to initialize. Verify it builds and runs by hitting CTRL+F5.
After the excitement over creating your first Blazor project fades, create your first component. Right-click on the Shared folder and choose Add then New Item, or press CTRL+SHIFT+A. Select Razor Component from the dialog and name it “Info.razor.”
Replace the default template with the following markup and code:
<h3>@TheDate: @Environment</h3>
<button **@onclick**="RefreshDate">Refresh</button>
@code {
public string Environment { get; set; }
public DateTime TheDate { get; set; }
protected override void OnInitialized()
{
Environment = System.Environment.OSVersion.VersionString;
RefreshDate();
}
public void RefreshDate()
{
TheDate = DateTime.Now;
}
}
The @code block contains properties and methods that are created as part of the component class. The @TheDate convention binds to the related property of the component. The button element illustrates how to bind to JavaScript events. Simply prefixing the event with an @ results in evaluating the target as a C# method instead of a JavaScript one. In the body of the code you can see a lifecycle event is triggered when the component is initialized and sets the OS version to show it’s being executed on the server.
Drop the component into the Counter.razor page in the Pages folder by adding this markup prior to the @code tag:
<Info></Info>
Run the application and open the debugging console (F12) and navigate to the network tab. Refresh the app (F5) and look for the WebSocket connection as shown here:
Next, click on the WebSocket to show messages. Navigate to the Counter page and click the Refresh button. You can watch the server dispatch updates.
Congratulations! You’ve created a Blazor app, built a component, and wrote your first lifecycle and data-binding code.
Blazor and WebAssembly: The Future
Blazor realizes the dream to run .NET anywhere and finally makes it possible to write single page applications using C#. The Blazor architecture is very similar to server-side ASP. NET MVC and Blazor features like dependency injection, middleware configuration, and Razor components should already be familiar to experienced developers. Although Blazor WebAssembly isn’t targeted to release until May of 2020, server-side Blazor is production-ready today and ships as part of the ASP. NET Core 3.0 release. Developers can get started now and immediately take advantage of the same web components they use in other projects.