If you experimented with Blazor during the initial days, you might have encountered snags and limitations that no longer exist. This post lists top ten things that you wish Blazor could do - that it actually does!
Introduction
Blazor is still a relatively young technology, yet it has gained incredible momentum since its release in 2018. Originally an experimental project, Blazor now ships as part of .NET 5 and is considered production-ready.
Its main attraction for .NET developers is that they can use their favorite languages like C# to write single-page applications, reusing existing .NET libraries and frameworks. Like most modern web technologies, Blazor is evolving rapidly. Every few months, its developers release previews packed with new features.
If you experimented with Blazor during the early days, you might have encountered snags and limitations that no longer exist. Blazor is tightly integrated with ASP.NET Core, supports all major .NET APIs not constrained by the browser’s security sandbox, and provides bi-directional integration with JavaScript. These capabilities make it a robust choice to build enterprise web applications.
If you still have your doubts about using Blazor, keep reading. Here are the top ten things you wish Blazor could do — that it actually does!
.NET 5 and C# 9
Blazor ships as part of .NET and supports all relevant APIs. Some APIs, such as file I/O, just don’t make sense in the browser context due to restrictions on accessing the local file system.
Blazor also supports the latest C# language version 9.0. Using the newest language constructs not only improves your code’s conciseness and readability, but it can also actually improve performance! Here’s a quick look at some helpful C# 9.0 features you can use in your Blazor apps.
Did you know you can save keystrokes with simplified object initialization? It’s no longer necessary to redeclare the class when you create a new instance. This redundant code:
Person person = new Person();
simplifies to this:
Person person = new ();
Use the new switch expression to simplify logic. What looked like this:
public int DoMath(int a, int b, Operator op)
{
switch(op)
{
case Operator.Add:
return a + b;
case Operator.Sub:
return a - b;
case Operator.Mult:
return a * b;
default:
return a / b;
}
}
You can now express more succinctly:
public int DoBetterMath(int a, int b, Operator op) =>
op switch
{
Operator.Add => a + b,
Operator.Sub => a - b,
Operator.Mult => a * b,
_ => a / b
};
Don’t forget you can also use the fast SpanT
for array and string
manipulation, as well as the new range operators.
Markdown
Markdown creates web-based documents as a popular and lightweight alternative to HTML. Its simple syntax makes it easy to write without having to worry about nested tags or special controls.
Repositories like GitHub and most blog engines and content management systems (CMS) support Markdown. Browsers lack native Markdown support and can only display it once it’s rendered into HTML. A variety of open-source libraries format, parse, validate, and transform markdown.
Building a markdown editor in Blazor is incredibly easy since it supports existing .NET libraries. The figure below demonstrates a simple Blazor WebAssembly
markdown editor that takes only minutes to create.
To see for yourself, create a new Blazor WebAssembly
project. If you’re not sure how, check out this learning module. Next, add a reference to the Markdig package. Update the Index page to include this row:
<div class="row">
<div class="col-6">
<textarea
@bind-value="@MarkdownText"
@bind-value:event="oninput"
rows="20"
cols="80">
</textarea>
</div>
<div class="col-6">
<textarea
@bind-value="@Html"
@bind-value:event="oninput"
rows="20"
cols="80">
</textarea>
</div>
</div>
In the code behind, implement properties like this:
private string markdown;
public string MarkdownText
{
get => markdown;
set
{
if (value != markdown)
{
markdown = value;
OnMarkdownChanged();
}
}
}
public string Html { get; set; }
The conversion is then as simple as:
private void OnMarkdownChanged() => Html = Markdown.ToHtml(markdown);
Now you have a real-time markdown editor that refreshes the HTML as you type.
gRPC
The popular new gRPC framework provides high-performance communication between endpoints. It uses the newer HTTP/2 protocol, which allows parallel requests over a single connection. The payload is a compressed binary format, so it usually has a much smaller network footprint than traditional REST or even WCF SOAP-based implementations.
According to this gRPC architecture document, gRPC can be up to eight times faster than JSON serialization and has 60 to 80 percent smaller payloads than equivalent REST services. gRPC is built into .NET Core, and the default, lightweight web server that ships with .NET Core (Kestrel) and supports HTTP/2.
Browser limitations make it impossible to implement a standard gRPC browser client because no APIs exist to control HTTP/2 communication. An open-source project called gRPC-Web provides an extension that functions in the browser and includes a proxy to handle requests from both implementations, making it possible to use gRPC from Blazor WebAssembly
applications.
After you install and configure the required packages, gRPC calls look almost identical to REST-based requests. Here’s an example call:
forecasts =
await WeatherForecastsClient.GetWeatherAsync(new WeatherForecast()).Forecasts;
gRPC requires all APIs to have a custom input and custom output. In the example above, the custom input WeatherForecast
indicates a request for the forecast, and the response contains the Forecasts
property payload.
You can see the complete end-to-end example in the blog post How to use gRPC-Web with Blazor WebAssembly on App Service. An open-source project even generates all required gRPC infrastructure from a set of interfaces. Check out the gRPC generator to see the “grWizard
” in action.
GraphQL
GraphQL is another popular protocol that is rapidly replacing traditional REST in modern web applications. Facebook created it to improve its user interface performance and designed it specifically to streamline communication from a front-end client. They did not intend it for interprocess communication like gRPC.
GraphQL is popular for its strongly-typed schema, easy discoverability, and capacity for a single request to aggregate responses from multiple back ends. Its most popular feature enables the client to define the response shape to receive only the specifically required data and avoid over-fetching.
Consider a data set that contains contact information. To implement an autocomplete search box that just returns names, you might make a request like this:
{
contact(filter: 'likness') {
lastname,
firstname
}
}
The request only returns names. In a different component, you might want to show more data and shape the request like this:
{
contact(filter: 'likness') {
lastname,
phone,
address {
street,
zipCode
}
}
}
Popular GraphQL libraries for .NET include GraphQL.NET and Hot Chocolate. To see GraphQL in action with a Blazor WebAssembly
app, follow the Get started with Strawberry Shake guide. A companion to HotChocolate (GraphQL on the server), Strawberry Shake creates .NET GraphQL client proxies to consume endpoints with strongly-typed payloads.
EF Core and Azure Cosmos DB
Entity Framework Core (EF Core) is an object-to-database mapper enabling developers to use domain objects and a consistent, strongly-typed data access API to manage data persistence. It supports various databases, including SQL Server, MySQL, PostgreSQL, SQLite, and Azure Cosmos DB. EF Core is cross-platform and targets .NET 5, so it is fully compatible with Blazor projects.
The caveat is the browser’s security sandbox: most databases use non-standard protocols over non-standard ports that browsers don’t support. Azure Cosmos DB is an exception because it provides the option to access APIs directly over HTTPS.
The EF Core Azure Cosmos DB provider can access the database directly from the client running in the browser. This has constraints, however. It is bad practice to store credentials, even encrypted credentials, on the client. You must assume the client has access and the information could leave your database wide open.
To secure access, secure the user’s identity with a provider like Azure Active Directory. Then, instead of using your secret key, you can request an Azure Cosmos DB resource token with security constraints.
Read EF Core and Azure Cosmos DB with Blazor WebAssembly to learn more.
Progressive Web Apps
It’s often challenging to deliver apps that work across all devices and deliver a native experience. Progressive web apps (PWA) provide a “best of all worlds” solution.
Using a special browser-based feature called a service worker, PWAs enable web apps to run disconnected. Install these apps just like ordinary apps on your phone, desktop, or tablet. Their code delivers pages from local storage when an active connection is not available. They also support push notifications, so your users never miss an important event.
The Blazor WebAssembly
app template has a checkbox
to enable PWA. From the command line, simply use the --pwa
switch, like this:
dotnet new blazorwasm --pwa -o pwa
After creating your new project, you’ll notice particular files under wwwroot.
- The manifest.json is a special configuration file that describes your app and the location of various icons to use when installed.
- Several icons ship with the template.
- The service-worker.published.js script contains the special code that enables offline mode.
The basic PWA app stores pages in local storage as the user navigates the site. It pre-installs essential pages and intercepts the browser’s fetch page mechanism. The interception code loads pages from the cache when the user is offline and refreshes the cache when the user is online. When you run the application, modern browsers provide the option to install it.
The figure below shows the icon and action in Microsoft Edge.
Installing the PWA app adds it to your applications list, places an icon to run it, and launches it in a chromeless window that looks like the figure below:
Although the app has its own window that you can move and resize, it is a version of the web browser dedicated to running the PWA.
CSS Isolation
The latest Blazor release features CSS isolation. Single-page application frameworks like Angular and React heavily use this powerful feature.
Instead of being forced to manage a single large CSS library for the entire app, CSS isolation enables you to style each component individually. There is no need to come up with unique names for each component type. If you style a heading element for “component A”, it only applies to that component and doesn’t conflict with “component B.”
The default Blazor project template generates examples of CSS isolation. To create a stylesheet isolated to your component, simply create a file with the same name as the component with the .css extension. In the default Blazor template, notice that MainLayout.razor has a related file named MainLayout.razor.css. The styles defined in that file only apply to the MainLayout
component.
To see how it works, notice that when you run your app, it references a file with the convention appname.styles.css. The figure below shows the result of examining network calls for an app named “pwa
.”
Opening the file reveals this code snippet:
.page[b-o6yozaou6y] {
position: relative;
display: flex;
flex-direction: column;
}
.main[b-o6yozaou6y] {
flex: 1;
}
Notice the styles now have a unique suffix to avoid collision with other components. For full CSS isolation details, read ASP.NET Core Blazor CSS Isolation.
Virtualization
Business apps commonly handle large data lists. Paging is not always an option or the optimal solution, but large lists can degrade performance due to the overhead of rendering the list’s components. Users, on the other hand, only use a subset of the data at any given time. Even if the list contains hundreds of thousands of elements, the app likely only displays a few dozen at once.
Virtualization takes advantage of this to render only visible components. For a list of 500,000 elements, the app only renders the 50 elements in view. As the user scrolls the list and other elements come into view, the app renders them. The overall time to process is the same, but delaying the render until the item scrolls into view gives users the perception of improved performance.
In Blazor apps, virtualization is extremely easy to implement. Consider a FoodItem
component that displays a list of food items. A typical list might render like this (notice the use of the @key
attribute to help Blazor keep track of list items).
<div style="height:500px;overflow-y:scroll">
@foreach (var foodItem in foods)
{
<FoodItem @key="foodItem.Id" Item="@foodItem" />
}
</div>
To virtualize this component, so the app only renders in-view items, simply wrap the list in the Virtualize
component.
<div style="height:500px;overflow-y:scroll">
<Virtualize Items="@foods">
<FoodItem @key="context.Id" Item="@context" />
</Virtualize>
</div>
Some virtualization guidelines relate to how you size components. You can also specify a delegate to provide items for streaming. Read ASP.NET Core Blazor Component Virtualization to learn more about virtualization.
Server-Side Pre-Rendering
A common single-page application challenge is the delay between serving the web page and rendering the data. The page template must load first, followed by asynchronous operations to fetch and render data. This can cause a poor user experience.
Server-side pre-rendering addresses this issue by constructing the page in a virtual Document Object Model (DOM) on the server then delivering it as static content. The client app then takes over. This creates a seamless experience for the end-user. Pre-rendering can also improve your website’s search engine optimization (SEO) by delivering content for search engines to index.
For pre-rendering to work, the app must be hosted. A static website doesn’t have the server capabilities to pre-render. An ASP.NET Core hosted app can host the Blazor WebAssembly
to render the first page of components on the first load. The document Pre-render and integrate ASP.NET Core Razor components details the configuration steps.
You don’t have to settle for pre-rendering at runtime in response to HTTP requests. Your app can pre-render as part of your build process and deliver a set of static assets that represent the initial website state. Learn more about this approach by reading Pre-render Blazor WebAssembly at build time to optimize for search engines.
Hot Reload
One of the hottest new Blazor features, and number one on this list, is Hot Reload. Web development can be iterative, and productivity depends on seeing the result of incremental changes as quickly as possible. The .NET team not only implemented this feature, but they made it blazing fast. If you’ve used reload in previous versions, toss your expectations out the window because you must see the updated reload for yourself.
The new Hot Reload feature is available in .NET 6 preview 3. Be sure to download and install that .NET version to take advantage of the performance improvements.
The feature in .NET 5 requires recompiling and reloading the entire app, which is far slower. Use:
dotnet --version
When we run that, we see:
6.0.100-preview.3.21202.5
Let’s see Hot Reload in action. Create a new Blazor project called HotReload
:
dotnet new blazorwasm -o HotReload
Go to the directory:
cd HotReload
Open the project in your favorite editor, like Visual Studio (VS) or Visual Studio Code (if you use Visual Studio, ensure you use v16.9 or later). Under properties, find and open the launchSettings.json file.
Under your web server of choice (defaults are either IIS Express or the name of your app), add the property hotReloadProfile
with the value blazorwasm
. Ours looks like this:
{
"iisSettings": {
},
"profiles": {
"IIS Express": {
},
"hotreload": {
"hotReloadProfile" : "blazorwasm",
"commandName": "Project",
"dotnetRunMessages": "true",
"launchBrowser": true,
}
}
}
Save your changes. From the command line, run using the new watch command, followed by the standard run command:
dotnet watch -- run
If you’re correctly set up, the first message should note that Hot Reload is active. While the app is running, open the project in your favorite integrated development environment (IDE) or open the pages using Notepad.
Go to Pages, then Index.razor, change “Hello, world!
” to “Hello, live refresh!
”, and save. You should see the app restart and deliver the new content. The first time is always slower, so next, edit the text “Welcome to your new app
” by adding “friend
” at the end. Save it, and you should almost immediately see results similar to the figure below:
You can learn more about this feature in the .NET 6 Preview 3 announcement.