Getting started with Blazor
Author:
Ahmed Youssef
Published: March 28, 2022
22 minutes to read
Blazor is a Single Page Application development framework. The name Blazor comes from the words Browser and Razor.
Blazor either runs server-side or client-side. With the server-side model it executes on a server and sends the rendered HTML and CSS to the browser. In the Blazor Server Model UI events are sent back to the server using SignalR then the UI changes are sent to the client and merged into the DOM.
Blazor client-side model runs in the browser by utilising WebAssembly. It does not require any plugin installed on the browser. Since WebAssembly is a web standard, it is supported on all major browsers and devices. Code running in the browser executes in the same security sandbox as JavaScript frameworks.
Why use Blazor
The first question you would ask straight away when you hear about Blazor is why Blazor? And why not use another JavaScript framework like React or Angular. This is not an easy question to answer. JavaScript frameworks have been there for a while and gained a lot of popularity and success. I’m not trying to convince you with Blazor over other UI frameworks here, but I’m going to list the features and capabilities that attract most people to Blazor and might be good reasons for you too to start using Blazor.
.NET Experience
This might be the primary selling point of Blazor. If you are a .NET developer with no or little JavaScript experience Blazor allow you to start developing web applications without having to learn a new technology. You can use libraries or frameworks that you already use with your .NET projects as long as they are compatible with .NET Standard.
WebAssembly
WebAssembly (abbreviated Wasm) is designed as a portable compilation target for programming languages, enabling deployment on the web for client and server applications.
In November 2017, Mozilla declared support in all major browsers after WebAssembly was enabled by default in Edge 16. The support includes mobile web browsers for iOS and Android.
JavaScript is a high-level language, flexible and expressive enough to write web applications and has a huge ecosystem that provides powerful frameworks, libraries, and other tools, whereas WebAssembly is a low-level assembly-like language with a compact binary format that runs in the browser with near-native performance.
Blazor supports JavaScript interoperability. Using Blazor does not mean “No JavaScript”. You can still call JavaScript functions from .NET apps and vice versa.
Code Reusability
With Blazor you are using .NET throughout your different application layers. Developers in your team can easily work with backend or frontend. This means that you can reuse code between your backend and frontend, use your common libraries across application layers and share API models. Think about utilities like string formatters, DateTime helpers, cryptography and a lot more. You can now have one library that you can use across all different projects - backend or frontend.
Server-side Rendering
One of the primary requirements that you would frequently be asked for when developing SEO friendly websites is server-side rendering. Server-side rendering is important when you build applications intended to be crawled by search engines as it allows bots to crawl your website contents without having to execute JavaScript code.
By using Blazor, server-side rendering is included as standard.
Blazor WebAssembly vs. Blazor Server
Blazor WebAssembly
In Blazor WebAssembly (or the client-side hosting model) the application runs directly in the browser on WebAssembly. So, the compiled application code itself, dependencies and the .NET runtime are downloaded to the browser. This means that the application will need some time to download and load the application and dependencies assemblies at start-up.
However, this means the app remains functional if the server goes offline.
Reference: ASP.NET Core Blazor hosting models
Blazor Server
In Blazor Server (or the server hosting model) the application is executed on the server from within an ASP.NET Core application. Between the client and the server, a SignalR connection is established. When an event occurs on the client such as a button click the information about the event is sent to the server over the SignalR connection. The server handles the event and the generated HTML. The entire HTML is not sent back again to the client, it’s only the diff that is sent to the client over the established SignalR connection. The browser then updates the UI.
Because code run on the server, the initial load is much faster than Blazor WebAssembly. The application can take advantage of server capabilities such as .NET Core APIs which allows utilizing the existing tooling such as debugging. However, the main concern with using this model is the high latency as every user interaction will require a network hop and this means also that there is no offline support.
Reference: ASP.NET Core Blazor hosting models
Create your first app
In this article, we’ll go through the steps of how to create and deploy a Blazor WebAssembly app. There are two types of Blazor WebAssembly apps, stand-alone and hosted models.
Standalone is suitable when you intend to deploy your app without an ASP .NET Core app to serve it, for example hosting the app on IIS server or Azure App Service (windows).
Hosted the app uses an ASP.NET Core server to host the client App which gives you the flexibility to host it in Azure App Service (Windows or Linux) and allow you to share code between client and server apps.
In this article, we’ll be using hosted Blazor WebAssembly.
Create the project
Visual Studio Code
For the command line run the following:
dotnet new blazorwasm --hosted -o FirstBlazorApp
Visual Studio
From Visual Studio Choose Blazor WebAssembly App template and make sure ASP.NET Core hosted option is selected.
Now open the project in your favourite editor and you will get the following folder structure:
You will have three projects created in your solutions.
- Shared a class library project referenced by both Client and Server and the purpose of this project is to include all the code shared between the client and server such as API models.
- Client the Blazor WASM project. It has all the code required to run your Blazor client App.
- Server an ASP.NET Core project. You can create your APIs in this project which will run in the same environment as your client app. This app also acts as a host server to run your client app. This is enabled in the
Program.cs
file by a simple line:
app.UseBlazorFrameworkFiles();
Run the project
To run the project:
- Go to Client folder (if you want to run the standalone client without the server) or Server folder (if you want to run both server and client).
- Run this command to start the app
dotnet run
- From the browser navigate to http://localhost:5173/
Walkthrough
Under Client folder we have the following:
_imports.razor
contains all the namespaces imported and used by the project.App.razor
is the main component of the app. It has theRouter
component which loads all the pages inside theRouteView
component. If the URL was not found the app displays the template inNotFound
component.
<Router AppAssembly="@typeof(App).Assembly">
<Found Context="routeData">
<RouteView RouteData="@routeData" DefaultLayout="@typeof(MainLayout)" />
<FocusOnNavigate RouteData="@routeData" Selector="h1" />
</Found>
<NotFound>
<PageTitle>Not found</PageTitle>
<LayoutView Layout="@typeof(MainLayout)">
<p role="alert">Sorry, there's nothing at this address.</p>
</LayoutView>
</NotFound>
</Router>
wwwroot
has the client-side assets and styles. In Blazor WebAssemblyindex.html
is the main entry point to the application. You can add any CSS or JavaScript references to this page. You can also redesign how the loading page should look during the initial load.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta
name="viewport"
content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no"
/>
<title>FirstBlazorApp</title>
<base href="/" />
<link href="css/bootstrap/bootstrap.min.css" rel="stylesheet" />
<link href="css/app.css" rel="stylesheet" />
<link href="FirstBlazorApp.Client.styles.css" rel="stylesheet" />
</head>
<body>
<div id="app">Loading...</div>
<div id="blazor-error-ui">
An unhandled error has occurred.
<a href="" class="reload">Reload</a>
<a class="dismiss">🗙</a>
</div>
<script src="_framework/blazor.webassembly.js"></script>
</body>
</html>
Shared
Under Shared folder, you will find theMainLayout.razor
and all other shared components. MainLayout has been set as the DefaultLayout in theApp.razor
.Pages
Under pages, you will find the components which will be rendered inside the MainLayout. In the page component, there are three main pieces you need to understand:@page
directive which defines the route Url for the current pagePageTitle
overrides the application page title.@code block
allows you to write your code in the same file with the razor template.
@page "/counter"
<PageTitle>Counter</PageTitle>
<h1>Counter</h1>
<p role="status">Current count: @currentCount</p>
<button class="btn btn-primary" @onclick="IncrementCount">Click me</button>
@code { private int currentCount = 0; private void IncrementCount() {
currentCount++; } }
Code-behind
As you can see in the previous example both the HTML markup and the C# code exist in the same file, which makes it easy and simple to build components in Blazor. However, as the business grows and requirements get bigger and more complex, splitting the code into a Code-behind file might be a good idea to keep the code clean and easy to maintain.
Splitting Blazor’s component code into a separate file is easy as all .razor
files become classes when the project is compiled. For example, the code inside the Counter.razor
file gets extracted into a class called Counter
when compiled.
To move the code inside a component into a separate class there are two methods to achieve that:
Partial Class
To create a partial class for the Counter component, create a new class file and call it Counter.razor.cs
then add the following code to it:
namespace FirstBlazorApp.Client.Pages;
public partial class CounterComponent
{
private int currentCount = 0;
private void IncrementCount()
{
currentCount++;
}
}
Now, you can remove the code block from Counter.razor
and build the project and you’ll find that it builds successfully with no errors.
Inheriting ComponentBase class
Instead of creating a partial class for your components, you can create a class that inherits from ComponentBase
class. You can then remove the code block from the .razor
file and use the @inherits
directive to inherit from the class you have just created.
Note that in this method you need to modify access modifiers from private to protected to be able to access them from the .razor
file.
Because the class inherits from ComponentBase
, another benefit you get is that you have access to the component’s lifecycle methods such as OnInitializedAsync()
.
You can find more details about component lifecycle in the following article: ASP.NET Core Razor component lifecycle
- Counter.razor.cs
using Microsoft.AspNetCore.Components;
namespace FirstBlazorApp.Client.Pages;
public class CounterComponent : ComponentBase
{
protected int currentCount = 0;
protected void IncrementCount()
{
currentCount++;
}
protected override Task OnInitializedAsync()
{
return base.OnInitializedAsync();
}
}
- Counter.razor
@page "/counter" @inherits CounterComponent
<PageTitle>Counter</PageTitle>
<h1>Counter</h1>
<p role="status">Current count: @currentCount</p>
<button class="btn btn-primary" @onclick="IncrementCount">Click me</button>
Scoped styles
Blazor supports scoping CSS to Razor components, which can simplify CSS and avoid collisions with other components or libraries.
To enable CSS isolation is simple. Create a .razor.css
file matching the name of the .razor
component file in the same folder. For example, to create isolated CSS for the Counter
component create a file called Counter.razor.css
in the same folder as Counter.razor
file.
The styles defined in Counter.razor.css
are only applied to the rendered HTML of the Counter
component.
For more details about Blazor CSS isolation see the following article:
ASP.NET Core Blazor CSS isolation
Writing unit tests
In unit tests, only the Razor component (Razor/C#) is involved. External dependencies, such as services and JS interop, must be mocked.
There’s no official Microsoft testing framework for Blazor, but Microsoft recommendsusing bUnit
which provides a simple and easy way to unit test Razor components.
The good news is that bUnit
works with common testing frameworks, such as MSTest
, NUnit
, and xUnit
which makes bUnit tests feel like regular unit tests.
Let’s go through the steps of how to start writing unit tests with bUnit using xunit testing framework:
- Create a xunit test project by running:
dotnet new xunit -o FirstBlazorAppTests
- Under FirstBlazorAppTests add reference to FirstBlazorApp project:
dotnet add reference ../Client/FirstBlazorApp.Client.csproj
- Add the following NuGet packages:
dotnet add package bunit.web
dotnet add package bunit.xunit
- Now you can start writing unit tests as you would normally do with any .NET project
The following is an example of a test class for the Counter
component:
using Bunit;
using Xunit;
using FirstBlazorApp.Client.Pages;
namespace FirstBlazorApp.Tests;
public class CounterComponentTests
{
[Fact]
public void CounterComponentTest()
{
// Arrange: Render the Counter component
using var ctx = new TestContext();
var cut = ctx.RenderComponent<Counter>();
// Act: Find and click the <button> element
cut.Find("button").Click();
// Assert: Find the <p> element, then verify its content
cut.Find("p").MarkupMatches(@"<p role=""status"">Current count: 1</p>");
}
}
For more details about bUnit see the official bUnit documentation:
Testing Blazor components
Deploy the Blazor app
In Blazor Server model a web server capable of hosting an ASP.NET Core app is required in order to run your Blazor app. However, in Blazor WebAssembly there are two deployment strategies:
- Standalone deployment
The app is hosted on a static web server where .NET is not used to serve the app. Any static file server can host the Blazor app. - Hosted deployment
The app is served by an ASP.NET Core app that runs on a web server. As you can see in the previous example, we have two main Projects (Client and Server). The Client project produces the static files required to run the app whereas the Server project contains the ASP.NET Core app that acts as a small server to host the client app.
Deploy to Azure App Service
You can publish and deploy the Blazor app directly from VS Code which is a convenient and efficient way to deploy Blazor apps during the development process. However, for production, you should always consider using Continuous Deployment to deploy your apps.
Deploy from VS Code
Let’s go through the steps for how to deploy FirstBlazorApp that we have just created to Azure App Service using VS Code.
- Install Azure App Service extension and configure it: Azure App Service VS Code Extension.
-
Publish the app. When you deploy Blazor WASM to Azure App Service windows you can use both strategies (Standalone or Hosted), however, if you wish to host the app on Azure App Service Linux you won’t be able to use the Standalone strategy.
- Standalone deployment (windows):
Publish the Client app to generate the deployment package:
dotnet publish Client -c Release -o ./publish
- Hosted deployment (Windows or Linux):
dotnet publish Server -c Release -r linux-x64 -o ./publish
- Standalone deployment (windows):
- Now right click on the publish folder that has been just created and select
Deploy to Web App…
Deploy from Azure DevOps
Finally, we’ll go through the steps of how to configure Azure DevOps pipelines to build, test and deploy our FirstBlazorApp to Azure App Service. To do so:
- First, push your code to a new repository.
- From the Azure Portal create an Azure App Service and set the runtime stack to
.NET 6
. - Go to Azure DevOps and create a new pipeline.
- Select your code repo and proceed to the Review step.
- Go to Project
Settings > Service
connections and create a new service connection then selectAzure Resource Manager
type then chooseService Principal (automatic)
and set scope level to subscription. - Click
Variables
and add the required variablesAzureServiceConnection
The name of the Service connection created in the previous step.ResourceGroup
The name of the resource group where your app service is created.WebAppName
The name of the App Service to deploy the app to.
- Paste the following YAML then click
Save and Run
trigger:
- main
pool:
vmImage: ubuntu-20.04
variables:
buildConfiguration: "Release"
dotNetVersion: "6.0.x"
location: "australiaeast"
dotNetFramework: "net6.0"
webAppDir: "$(Build.SourcesDirectory)/Server"
webAppTestDir: "$(Build.SourcesDirectory)/Tests"
steps:
- task: UseDotNet@2
displayName: Use .NET 6.0
inputs:
packageType: "sdk"
version: "6.0.x"
- script: dotnet build --runtime linux-x64 --configuration $(buildConfiguration)
workingDirectory: "$(webAppDir)"
displayName: "Building App..."
- task: DotNetCoreCLI@2
displayName: "Testing App..."
inputs:
command: test
projects: "$(webAppTestDir)/*.csproj"
arguments: "--configuration $(buildConfiguration)"
publishTestResults: true
- task: DotNetCoreCLI@2
displayName: "Publishing App..."
inputs:
command: publish
workingDirectory: "$(webAppDir)"
publishWebProjects: false
zipAfterPublish: True
arguments: "--runtime linux-x64 --configuration $(BuildConfiguration) --output $(Build.ArtifactStagingDirectory)"
- task: AzureRmWebAppDeployment@4
displayName: "Azure App Service Deploy"
inputs:
azureSubscription: "$(AzureServiceConnection)"
ResourceGroupName: "$(ResourceGroup)"
appType: webAppLinux
WebAppName: "$(WebAppName)"
Package: "$(Build.ArtifactStagingDirectory)/**/*.zip"