We explore how to make Blazor web apps run on Desktop.
Blazor is one of the most exciting technologies for web developers on the .NET stack and allows for building client/server-side web apps entirely in C#. Blazor isn't just for web apps though and has clear implications for desktop/mobile.
Turns out, there are multiple ways of building modern desktop apps with Blazor. The techniques are nothing now—what's called for is a shell which hosts a desktop browser sandbox, that runs regular Blazor apps, just as if running on the web. The key to successful hybrid desktop apps, however, is in the implementation and managing resource bloat. And as one can see with the first .NET 6 Preview, Blazor on desktop has been a reality and the story only gets better. The lure is simple—use Razor syntax and the familiar Blazor component model towards building native desktop apps.
Let's take a closer look at desktop apps powered by Blazor—we'll explore two popular ways for some clarity and see the desktop shells in action.
The desire to see web apps running on desktop has been long running. And given how exciting Blazor has been for .NET web developers, there has been a lot of zeal to make Blazor apps power desktop solutions. Enter the most ubiquitous solution for such needs—Electron. ElectronJS is an open source project to build cross-platform apps with web technologies and can target any desktop—Windows, Mac OS and Linux. Many of the most heavily-used desktop apps are essentially web apps wrapped inside the Electron shell, like Visual Studio Code, Microsoft Teams, Slack and Figma. Electron has been around for a while and gains credibility from a strong developer community/ecosystem.
Can Blazor web apps be wrapped inside the Electron shell to transform them into desktop apps? You bet.
First up, we start a Blazor server-side web project running on .NET Core 3.1. Next, we add the ElectronNET.API NuGet package that will enable the Blazor app to be bootstrapped within Electron.
In our Program.cs, we enable the use of Electron with the function UseElectron()
within the CreateHostBuilder()
method.
Next up, we head over to the Startup.cs file and arrange for the Blazor app to be bootstrapped within the Electron shell. Notice how we're setting up a desktop window with dimensions and title, and firing it up.
To bootstrap our Blazor app within Electron, we need to install the Electron.NET CLI tool. This is done from Terminal/Command Prompt of course.
While we can install the Electron.NET CLI globally, one needs to jump inside the Blazor project and initialize the Electron app—this drops a electron.manifest.json file within the project.
The last step should create a few configurations; but if not, the launchSettings.json file can be manually updated.
We're now ready to run our Blazor app within the Electron shell. Time for the three little words, from within the project directory:
If everything is set up right, the CLI command will show the Blazor app running on specified Localhost port—and a few things more.
Voila. Electron launches the desktop window with given dimensions and hosts the Blazor app within it. Just magical.
Yes, you get a fully functional Blazor app—just hosted as a desktop application, running web code. And thanks to Electron, this would work on Windows, Mac OS and Linux. Hallelujah!
Electron is pretty awesome. However, there is a small hesitation. And this stems from today's consciousness that a majority of people run a modern evergreen browser on their computers. One of Electron's key selling points may now be sowing the seeds of doubt in the minds of developers.
At the core of Electron's benefit as a shell to host web apps is the idea of a stable and predictable environment. To that end, every Electron app comes bundled with two things that provide dependability:
Let's keep in mind though that Electron is battle-tested and provides developers extensive APIs for the native system integration. Some of Electron's key benefits include direct access to file-system, running processes outside sandbox, kiosk mode, screen recording, system-tray support for minimized apps and much more. If Electron is serving your desktop needs today, there is no reason to look elsewhere.
However, what if today we assumed a stable computing environment and the presence of a modern browser? Could a shell like Electron do away with bundling Chromium and Node.JS? This is not an easy question to answer, since we are essentially trading stability for a smaller footprint. Perhaps your desktop app does not need deep integrations with the OS.
Steve Sanderson from the Blazor team wrote a detailed post with comparisons of app size and memory impact when Chromium and Node.JS are not bundled. It does beg the question as to whether lighter web shells could do the job of hosting modern Blazor apps with a level of consistency. Time will tell, but thankfully there are some alternatives to play around with.
How do Blazor web developers reach mobile or desktop land? An elegant experimental solution is Blazor Mobile Bindings, enabling developers to build cross-platform native/hybrid apps with Razor syntax and the Blazor component model. Slated to be eventually a part of proposed .NET MAUI evolution with LTS .NET 6, Blazor Mobile Bindings sit on top of Xamarin.Forms technology stack and can easily reach desktop—Windows through WPF renderers and MacOS through Mac renderers for Xamarin.Forms.
Having a way to reach desktop while writing Blazor code, Blazor Mobile Bindings opens up hosting hybrid views—essentially a web shell inside the bootstrapped app that can run a web code. Sound familiar? This time though, we'll use the new BlazorWebView—a lightweight alternative that does not include Chromium or Node.JS.
First up is getting the CLI template and firing up a new hybrid 'Hello World' Blazor Mobile Bindings project.
This scaffolds a solution with several platform-specific projects—a .NET Standard library and projects for iOS, Android, Windows and MacOS. Here is a trimmed down version highlighting the MacOS project:
The shared project dependencies show reliance on Xamarin and Mobile Blazor Bindings, not much else.
Essentially, this project is a combination of native and web UI lighting solutions for various mobile/desktop platforms. In the root folder of the shared project, App.cs provides the main UI entry point and things start up just as they do for every Xamarin.Forms app through Mobile Blazor Bindings. CounterState provides a component/service that would be used to keep track of click counters in both native/hybrid code.
Inside the shared project is a folder named WebUI—this is meant to house the Blazor app, with all the code and components as expected from a Blazor web project. There is also a wwwroot folder serving up static content, as one does for a regular Blazor web app.
Now we get to best part—Main.razor. This serves as the main UI rendered through Blazor, but is a combination of native and hybrid UI.
The code above almost reads like XAML, but with a few twists. The container is native through Xamarin.Forms, as is the first StackLayout
containing the label and the button. But then comes BlazorWebView
—the modern webview meant to host a full Blazor app or any other web app inside. And where do we get the Blazor app to host? The WebUI folder of course. You can see how nifty it is to have a full Blazor app within the project and weave in native UI, along with hosting hybrid web UI through the BlazorWebView.
The last piece of the puzzle is in the desktop specific projects for Windows or MacOS, using the corresponding renderers. For MacOS, this is no different from how the Xamarin.Forms renderers for MacOS bootstrap a desktop app—define an NSWindow
with appropriate settings, initialize Xamarin.Forms and allow Forms to paint the UI inside the window.
That's it. Time to set the desktop Windows/MacOS projects as startup and fire things up. Out comes a shiny desktop app showing the mix of native and hybrid UI. Hallelujah!
Notice how the Counter component is shared between native Xamarin.Forms UI and the corresponding Blazor component—they remain in sync through both interfaces.
So, you have a functional desktop app within a web shell that is hosting a full Blazor web app. Technology is beautiful.So, you have a functional desktop app within a web shell that is hosting a full Blazor web app. Technology is beautiful.
Blazor is exciting and enables .NET web developers to build modern web apps with C#. Blazor invites developers into a rich ecosystem seeing lots of innovation and availability of polished UI component frameworks like Telerik UI for Blazor. However, Blazor does not need to be for web apps only and can play easily in the desktop space.
Blazor apps can be effortlessly wrapped inside Electron to make compelling and consistent desktop solutions. However, modern webviews provide a lightweight alternative to Electron, thus minimizing the footprint of Blazor apps running on desktop. Blazor Mobile Bindings provides an experimental way to write native/hybrid cross-platform apps using Razor syntax and the Blazor component model. The new BlazorWebView component allows for easy hosting of any web content, in particular, full Blazor apps as intended for the web. Modern technology stacks allow developers to mix and match coding paradigms and cross application platform barriers for added portability.
Upwards and onwards to what the future holds.
Sam Basu is a technologist, author, speaker, Microsoft MVP, gadget-lover and Progress Developer Advocate for Telerik products. With a long developer background, he now spends much of his time advocating modern web/mobile/cloud development platforms on Microsoft/Telerik technology stacks. His spare times call for travel, fast cars, cricket and culinary adventures with the family. You can find him on the internet.