ASP.NET, Blazor, Electron, .NET

Building Desktop Apps with Blazor

With the recent official release of .NET Core 3, I thought I'd take a stab at a fun little experiment. I love using Electron to create web-powered desktop apps. And I wanted to learn more about Blazor, the newest addition to the ASP.NET family. In this post I will show you exactly how to get started with Blazor and Electron in under 15 minutes!

UPDATE: This post is featured in C# Digest #284 and I will be giving a talk about Desktop Apps with Blazor in Baltimore on November 20th!


Prerequisites

Be sure to install both the .NET Core SDK and Node.js (you can check versions using dotnet --version and node --version). We need .NET Core 3.0 because we're going to scaffold out the Blazor application using dotnet new. We also need .NET Core 3.0 for Electron.NET, the library that allows for Electron & .NET integration. Node.js is used to download, configure, and integrate with the actual Electron instance.

Setup

Setup requires a few simple steps. While most steps are simply commands, there are 2 files that require manual edits. And once your project is setup, there's just one command to start it up!

Commands

mkdir blazor-electron-demo
cd blazor-electron-demo
dotnet new blazorserver --no-https
dotnet add package ElectronNET.API
dotnet new tool-manifest
dotnet tool install ElectronNET.CLI
dotnet electronize init

That's quite the block of commands, but they are all pretty simple and don't require any user interaction. Let's breakdown what each command is doing:

mkdir blazor-electron-demo 
cd blazor-electron-demo
Creates a folder for our project and sets it as the current working directory. Be sure to change this to your preferred project name!
dotnet new blazorserver --no-https
Scaffolds out a new server-side Blazor app, without support for HTTPs.
dotnet add package ElectronNET.API
Installs the NuGet package that adds Electron support to ASP.NET apps.
dotnet new tool-manifest
Adds support for locally installed `dotnet` tools.
dotnet tool install ElectronNET.CLI
Installs the required `electronize` tool locally to the project.
dotnet electronize init
Sets up Electron.NET manifest and updates launch profiles.

All commands should be executed in order and one at a time. Once they all complete, you are just two file changes away from running the application!

File Changes

In order for the ASP.NET application (Blazor app, in this case) to integrate with Electron, we need to make changes to Program.cs and Startup.cs.

Inside Program.cs:

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using ElectronNET.API; // ADD THIS!

namespace blazor_electron_demo
{
    public class Program
    {
        public static void Main(string[] args)
        {
            CreateHostBuilder(args).Build().Run();
        }

        public static IHostBuilder CreateHostBuilder(string[] args) =>
            Host.CreateDefaultBuilder(args)
                .ConfigureWebHostDefaults(webBuilder =>
                {
                    webBuilder.UseStartup<Startup>()
                              .UseElectron(args);  // ADD THIS!
                });
    }
}

Inside Startup.cs:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using blazor_electron_demo.Data;
using ElectronNET.API; // ADD THIS!

namespace blazor_electron_demo
{
    public class Startup
    {
        public Startup(IConfiguration configuration)
        {
            Configuration = configuration;
        }

        public IConfiguration Configuration { get; }

        public void ConfigureServices(IServiceCollection services)
        {
            services.AddRazorPages();
            services.AddServerSideBlazor();
            services.AddSingleton<WeatherForecastService>();
        }

        public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
        {
            if (env.IsDevelopment()) {
                app.UseDeveloperExceptionPage();
            } else {
                app.UseExceptionHandler("/Error");
            }

            app.UseStaticFiles();
            app.UseRouting();
            app.UseEndpoints(endpoints =>
            {
                endpoints.MapBlazorHub();
                endpoints.MapFallbackToPage("/_Host");
            });

            // ADD THIS!
            Task.Run(async () => await Electron.WindowManager.CreateWindowAsync());
        }
    }
}

With these two files updated we are now ready to run our app inside of Electron!

Usage

Run

Our app can be ran in two separate ways. Either with via the command line or through Visual Studio's "Run" dialog (thanks to the launch profile added during the setup using dotnet electronize init).

dotnet electronize start
Blazor running in a desktop app!

Debug

Once running, you can attach the debugger from Visual Studio's Debug > Attach to Process... and filtering by the project name. Breakpoints will work exactly as expected when running a .NET app. You can even debug the code inside the Razor files! The only downside is when you need to make a change you'll have to stop the app, edit the changes, and restart it. We'll talk more about that a little later.

Publish

Thanks to Electron.NET, we can publish our application to a platform-specific installer in one command!

dotnet electronize build /target win
# OR
dotnet electronize build /target osx
# OR
dotnet electronize build /target linux
Windows Note: I did experience an exception about The given key 'target' was not present in the dictionary. while running the command from a mingw64 prompt. Try the command again using a native cmd or powershell prompt.

This command will create a new installer under the ./bin/desktop folder. Inside you will find the installer for your platform!

Sample Code

I've created a GitHub repo demonstrating all of the changes we just made. Use it as a resource if you are having trouble, or reach out to me on Twitter!

Caveats

While this union of two technologies is fun, it does have its drawbacks. Ranging from developer workflow issues to API mismatch, these issues may be annoying but they are by no means a deal-breaker to using Blazor and Electron.

Developer Workflow

The most severe issue is the slow iterative development process. At this time the developer workflow involves starting the app, manually attaching the debugger, testing the app, then stopping the app to make changes and finally restart the cycle. Without the ability to hot reload after changes this makes development significantly more difficult. The maintainers of Electron.NET recommend developing the app in the browser for the majority of tasks, and only switch to electron for periodic testing.

Electron API/Interop

Electron.NET is young but comprehensive. Creating a functional bridge from C# & .NET to JavaScript & Node.js/Electron is no small feat. But there is a high chance that you will find a method or event that is not implemented. Remember that Electron.NET is a free library created by people in their spare time. Every single feature may not be present, but that's where open source shines. If you found a hole in the API or a feature that needs some work, then fork the project and try finding the fix yourself. Most of the time building software is a collaborative effort, and it can be very rewarding to help solve the issues rather than just waiting for them to be fixed.

Summary

We created a brand new Blazor project, added support for Electron thanks to Electron.NET, and created an installer ready for release! While some caveats do exist when building with a dev stack this cutting edge, there is absolutely nothing stopping you from building incredible desktop applications using Blazor. And with help from developers like you, Electron.NET will continue to improve!

Special shout out to Rob Horner and John Sedlak for his help editing this article! Thanks Rob and John!


As always, if you liked the article let me know on Twitter! If you had any issues or didn't understand something, let me know that too! I truly appreciate the feedback!

Author image

About Jim Buck

Jim is a leading Software Engineer/PM in the Baltimore Area, specializing in ASP.NET Core, NodeJS, and Azure. Passionate about open source, he enjoys building tools and speaking about tech.
  • Baltimore, MD