Blazor WebAssembly is Microsoft’s framework for building single-page apps (SPAs) with C# and Razor. You can use Blazor to build more or less the same experiences as with other SPA frameworks such as React or Angular. This includes Progressive Web Apps (PWA)—web-based applications that work offline and can be installed on the user’s system.
Project Fugu, a joint venture between Microsoft, Google, Intel, and others, wants to bring more capabilities to the web to make PWAs and other web-based applications even more powerful. This includes accessing the file system, displaying app badges, or registering a web app as a handler for specific file extensions. As web platform APIs are for everyone, they can also be used within Blazor WebAssembly applications.
However, those APIs are typically JavaScript-based. To access them within Blazor WebAssembly apps, they must be wrapped for .NET. Some ready-to-use open-source NuGet packages exist, but my Thinktecture colleague Patrick Jahr and I missed some APIs. So, over the last couple of weeks, we implemented several missing Fugu wrappers for Blazor WebAssembly. You can find the source code and all samples on GitHub: https://github.com/thinktecture/Thinktecture.Blazor
This blog post will introduce some of the new web platform APIs, including their browser and platform support, and explains how to use the Blazor WebAssembly wrapper packages provided by the community and us. Please note that our packages are technical showcases and not official Thinktecture products.
Stay backwards compatible with Progressive Enhancement
Before using any of the following APIs, you should always check if it is available on the target system, as the platform support varies. Calling a non-existing API will lead to an error during runtime, and the application may crash. To avoid this, all wrapper packages discussed in this blog post offer a method called IsSupportedAsync()
returning a boolean value whether the API is available:
var isSupported = await badgingService.IsSupportedAsync();
if (isSupported)
{
// enable badging feature
}
else
{
// use fallback mechanism or hide/disable feature
}
In case the API is not available on the user’s system, you should hide or disable the feature. In some cases, alternative implementations are available to achieve similar behavior.
Share content to other apps with the Web Share API
The Web Share API allows you to share a title, URL, text, or files with another application installed on the user’s system via the share functionality provided by the operating system.
To use this API from your Blazor WebAssembly application, install the wrapper NuGet package for the Web Share API by using the following command:
dotnet add package Thinktecture.Blazor.WebShare
Before using the API, you have to add the WebShareService
to the IServiceCollection
in Program.cs as follows:
builder.Services.AddWebShareService();
Now, you can inject the WebShareService
into your Blazor component. By calling its ShareAsync()
method and passing a WebShareDataModel
object to it, the device’s share sheet will open. The user can select an already installed application to share the content with or send it to another device via nearby sharing or AirDrop.
try
{
var data = new WebShareDataModel
{
Title = "Test 1",
Text = "Lorem ipsum dolor...",
Url = "https://thinktecture.com"
};
await webShareService.ShareAsync(data);
}
catch (Exception ex)
{
// data not shareable or user denied sharing
}
Please note that the API is only exposed on secure contexts (sites transferred via HTTPS and localhost) and can only be invoked due to user interaction (i.e., a keypress or click). This also applies to most other APIs in this article. If the data cannot be shared or the user has dismissed the share sheet, an exception will be thrown.
Details about sharing files can be found in the readme file of the Web Share API wrapper package.
The Web Share API is supported by Chrome 89, Edge 81, and Safari 12.1. Mozilla Firefox also supports the API, but only on Android. For more details, see the browser support table for the Web Share API on caniuse.com.
Access the clipboard via the Async Clipboard API
The Async Clipboard API allows you to read and write text, images, and other data from or to the system’s clipboard. The supported data formats depend on the target platform. For instance, WebKit currently only supports plain text, HTML, URI lists, and PNG images.
You can install this wrapper package by using the following command:
dotnet add package Thinktecture.Blazor.AsyncClipboard
Before using the API, you have to add the service to your service collection:
builder.Services.AddAsyncClipboardService();
Afterward, you can inject the service into your Blazor component. It offers two shorthand methods for writing and reading plain text to or from the clipboard:
// Write plain text to the clipboard
await asyncClipboardService.WriteTextAsync("Hello world");
// Read plain text from the clipboard
var text = await asyncClipboardService.ReadTextAsync();
The service also offers WriteAsync()
and ReadAsync()
methods for dealing with arbitrary data, if supported by the target platform. The readme file of the Async Clipboard API wrapper package explains those two methods in more detail.
Depending on the platform and action, permission or confirm prompts may open.
The Async Clipboard API is supported since Microsoft Edge 79, Google Chrome 62, and Apple Safari 13.1. Mozilla Firefox only supports writing plain text, since version 63. For more details, see the browser support table for the Async Clipboard API on caniuse.com).
Opening and saving files with the File System Access API
The File System Access API allows your app to open, modify, and save local files and directories. It is based on the more general File System API and specifically deals with the local file system. This allows developers to finally bring traditional productivity apps to the web, such as image editors, word processors, or IDEs. For instance, the web-based version of Visual Studio Code uses this API to access local code workspaces.
Kristoffer Strube maintains wrappers for both the File System API and the File System Access API. His implementations inspired all our wrappers, and some rely on his packages. We want to take this opportunity to thank Kristoffer for the great collaboration so far.
To install this wrapper, run the following command:
dotnet add package KristofferStrube.Blazor.FileSystemAccess
Next, the File System Access API service needs to be added to your service collection:
builder.Services.AddFileSystemAccessService();
Now you can inject the IFileSystemAccessService
into your Blazor component or service. To get a file handle, call the ShowOpenFilePickerAsync()
method. If the user selects a file via the file picker, it will then be passed to the application, where you can access its contents:
try
{
var fileHandles = await _fas.ShowOpenFilePickerAsync();
var fileHandle = fileHandles.Single();
var file = await fileHandle.GetFileAsync();
var text = await file.TextAsync();
Console.WriteLine(text);
}
catch (Exception ex)
{
// API not available, user denied request or file could not be opened/read
Console.WriteLine(ex);
}
With the help of the file handle, you can also modify its contents. Additional usages of this API and package are shown in the readme file of the File System Access API wrapper package.
The File System Access API is supported by Chromium-based browsers only: Microsoft Edge and Google Chrome since version 86. Browser vendors can decide to disable the API, though. For example, Brave chose not to support the API, although this browser is also Chromium-based. Apple Safari only supports the File System API with the Origin Private File System (OPFS), an isolated file system that isn’t mapped to the local one. Mozilla Firefox supports neither of them. For more details, see the browser support table for the File System Access API on caniuse.com. If you need to support all browsers, refer to fallback approaches.
Registering your app as a file editor via File Handling
The File Handling API is built on top of the File System API. It allows your PWA to register as a file handler for specific file types. This requires your app to be installed on the user’s system, as the app has to register its name and icon with the operating system. On desktop systems, installation is currently only supported by Chromium-based browsers.
To use the File Handling API in your Blazor WebAssembly app, install the following NuGet package:
dotnet add package Thinktecture.Blazor.FileHandling
Next, you need to add the file handling service to the service collection:
builder.Services.AddFileHandlingService();
In contrast to all other APIs, the File Handling API has an imperative and a declarative part. To be registered as a file handler on installation, you must declare the file_handlers
property in your application’s Web Application Manifest:
{
"file_handlers": [{
"action": "./",
"accept": {
"text/plain": [".txt"]
}
}]
}
This property takes an array of file handlers which must include an action (the URL that should be called if the application is invoked as a file handler) and an accept object. This object maps the list of file extensions to a media type. A handler can deal with more than one media type and file extension. Optionally, you can also specify an icon for this file type.
The second part involves adding code to your application: This code is executed when your application is launched as a file handler. Therefore, we are exposing the method SetConsumerAsync()
on the FileHandlingService
. This method takes a callback with launch parameters. The callback is invoked if the application was launched as a file handler. It will then expose the file handles on the Files
property. The type is fully compatible with the File System Access API types.
await _fileHandlingService.SetConsumerAsync(async (launchParams) =>
{
foreach (var fileSystemHandle in launchParams.Files)
{
if (fileSystemHandle is FileSystemFileHandle fileSystemFileHandle)
{
var file = await fileSystemFileHandle.GetFileAsync();
var text = await file.TextAsync();
Console.WriteLine(text);
}
}
});
Using the file handles, the application can access the files’ contents and display them during runtime.
The File Handling API is only supported by Chromium-based browsers since version 102 (see Chrome 102 release notes).
Communicate unfinished tasks via the Badging API
The Badging API allows you to display a badge on the installed application’s icon as a less obtrusive alternative to notification banners. The badges are well known from applications like email clients, instant messengers, or to-do apps. This also requires your application to be installed so it has an icon in the taskbar or dock.
To use this API, install the following wrapper package:
dotnet add package Thinktecture.Blazor.Badging
As always, the services needs to be added to the service collection:
builder.Services.AddBadgingService();
SetAppBadgeAsync()
method. This method takes a number that will be shown on the badge. You can also set it to null
. In this case, a generic badge will be shown (without a number).
await badgingService.SetAppBadgeAsync(3);
To remove the badge again, for example, when all email messages are read, all instant messages are answered, or all todo items are done, simply call the ClearAppBadgeAsync()
method:
await badgingService.ClearAppBadgeAsync();
The Badging API is currently only supported by Chromium-based browsers since version 81 on macOS and Windows, and by Apple Safari on iOS and iPadOS starting from version 16.4 Beta 1 (as long as the application has been added to the home screen and has permission for receiving push notifications). For more details, see the browser support table for the setAppBadge()
method on caniuse.com.
Powerful APIs for a more capable web
The new web platform APIs introduced by Project Fugu make web applications significantly more powerful, including your Blazor WebAssembly apps. Using the wrapper packages provided by the community and us, you can genuinely add superpowers to your Blazor-based web apps. We look forward to your feedback and are eager to see where our packages will be used. Keep an eye on our blog for more wrapper packages and updates on Progressive Web Apps. So let’s go and make the web a more powerful place!