Codementor Events

Write a Desktop app with React, Typescript, ASP.NET Core and WebView2

Published Jun 11, 2021
Write a Desktop app with React, Typescript, ASP.NET Core and WebView2

I was recently contacted by a client to rebuild a very old Microsoft Access application.
They didn't ask for any specific framework or platform to use so I was thinking to do it with WPF or the old good Windows Forms.

I have recently been working mainly on web applications with React as frontend and NodeJS or ASP.NET Core as backend and I found the development experience quite pleasant.

I search on the internet for a way to make a normal React/ASP.NET Core application as a Desktop one.

I found several libraries and frameworks but they all seem too heavy and I want a development experience very similar to the web one.

While I was searching I found a new technology done by Microsoft called WebView2 that is based on Chromium rendering engine:
https://docs.microsoft.com/en-us/microsoft-edge/webview2/

It's a quite remarkable technology.

If you bring together WebView2 on a Windows Forms application and spin an instance of ASP.NET Core with Kestrel, you get a full desktop application that can run like a web application.

Let's do it! 😃

Create a new ASP.NET Core React application

You need to have installed on your machine NodeJS and ASP.NET Core

Let's create a new ASP.NET Core React app with the following command:

dotnet new react -o my-new-app
cd my-new-app
dotnet build
dotnet run

Now if we open the browser at https://localhost:5001/

We have the basic ASP.NET Core / React app.

1_XUi2mtoGtK4xVB1Fc9Jiig.png

Let's use Typescript

Now it's time to replace the React app using Javascript with another React app using Typescript. This will make our app more robust and easier to maintains.

Let's stop the server with CTRL+C and delete the folder ClientApp at the root of the app (my-new-app).

Now open the command prompt from the root of the app and run the following command:

npx create-react-app client-app --template typescript

Now rename the newly created folder client-app to ClientApp.

If you run it again you'll see the empty Create React App:
1_4Tzi3ay3xUrTgeUCJwFwXQ.png

Let's make it a desktop application

Let's install WebView2 from a command prompt on the root of the app:

dotnet add package Microsoft.Web.WebView2

Now change my-new-app.csproj as in the following screenshot:
1_mcUFWZ5TLrwzbvykC2igCw.png
and create the following files at the root of the app:

frmMain.cs

using System.Windows.Forms;
namespace WinFormsApp
{
    public partial class frmMain : Form
    {
        public frmMain()
        {
            InitializeComponent();
        }
    }
}

frmMain.Designer.cs

namespace WinFormsApp
{
    partial class frmMain
    {
        /// <summary>
        ///  Required designer variable.
        /// </summary>
        private System.ComponentModel.IContainer components = null;
/// <summary>
        ///  Clean up any resources being used.
        /// </summary>
        /// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
        protected override void Dispose(bool disposing)
        {
            if (disposing && (components != null))
            {
                components.Dispose();
            }
            base.Dispose(disposing);
        }
#region Windows Form Designer generated code
/// <summary>
        ///  Required method for Designer support - do not modify
        ///  the contents of this method with the code editor.
        /// </summary>
        private void InitializeComponent()
        {
            this.webView21 = new Microsoft.Web.WebView2.WinForms.WebView2();
            ((System.ComponentModel.ISupportInitialize)(this.webView21)).BeginInit();
            this.SuspendLayout();
            // 
            // webView21
            // 
            this.webView21.CreationProperties = null;
            this.webView21.DefaultBackgroundColor = System.Drawing.Color.White;
            this.webView21.Dock = System.Windows.Forms.DockStyle.Fill;
            this.webView21.Location = new System.Drawing.Point(0, 0);
            this.webView21.Name = "webView21";
            this.webView21.Size = new System.Drawing.Size(1280, 768);
            this.webView21.Source = new System.Uri("https://localhost:5001/", System.UriKind.Absolute);
            this.webView21.TabIndex = 0;
            this.webView21.ZoomFactor = 1D;
            // 
            // Form1
            // 
            this.AutoScaleDimensions = new System.Drawing.SizeF(8F, 20F);
            this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
            this.ClientSize = new System.Drawing.Size(1280, 768);
            this.Controls.Add(this.webView21);
            this.Name = "frmMain";
            this.Text = "Desktop React App";
            this.ShowIcon = false;
            ((System.ComponentModel.ISupportInitialize)(this.webView21)).EndInit();
            this.ResumeLayout(false);
}
#endregion
private Microsoft.Web.WebView2.WinForms.WebView2 webView21;
    }
}

frmMain.Designer.cs

<root>
  <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
    <xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
    <xsd:element name="root" msdata:IsDataSet="true">
      <xsd:complexType>
        <xsd:choice maxOccurs="unbounded">
          <xsd:element name="metadata">
            <xsd:complexType>
              <xsd:sequence>
                <xsd:element name="value" type="xsd:string" minOccurs="0" />
              </xsd:sequence>
              <xsd:attribute name="name" use="required" type="xsd:string" />
              <xsd:attribute name="type" type="xsd:string" />
              <xsd:attribute name="mimetype" type="xsd:string" />
              <xsd:attribute ref="xml:space" />
            </xsd:complexType>
          </xsd:element>
          <xsd:element name="assembly">
            <xsd:complexType>
              <xsd:attribute name="alias" type="xsd:string" />
              <xsd:attribute name="name" type="xsd:string" />
            </xsd:complexType>
          </xsd:element>
          <xsd:element name="data">
            <xsd:complexType>
              <xsd:sequence>
                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
                <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
              </xsd:sequence>
              <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
              <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
              <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
              <xsd:attribute ref="xml:space" />
            </xsd:complexType>
          </xsd:element>
          <xsd:element name="resheader">
            <xsd:complexType>
              <xsd:sequence>
                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
              </xsd:sequence>
              <xsd:attribute name="name" type="xsd:string" use="required" />
            </xsd:complexType>
          </xsd:element>
        </xsd:choice>
      </xsd:complexType>
    </xsd:element>
  </xsd:schema>
  <resheader name="resmimetype">
    <value>text/microsoft-resx</value>
  </resheader>
  <resheader name="version">
    <value>2.0</value>
  </resheader>
  <resheader name="reader">
    <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
  </resheader>
  <resheader name="writer">
    <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
  </resheader>
</root>

Now append to Program.cs the following code:

Application.SetHighDpiMode(HighDpiMode.SystemAware);
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new frmMain());

Like in the following screenshot:

1_aX6fF4MufxHt4-9M_OSTyA.png

Let's build our desktop React app

Now is time to see our basic app running as a desktop application.

From the command prompt run the following commands to build the app:

dotnet restore "./my-new-app.csproj" --runtime win-x64
dotnet publish "my-new-app.csproj" -c Release -o ./publish --no-restore --runtime win-x64 --self-contained true /p:PublishTrimmed=true /p:PublishSingleFile=true

This command will generate an executable of the application self-contained. It means you don't need to install dotnet core on the machine to run it. 😃

Be aware it will take a bit of time the first time you build.

After it compiles you'll see a new folder publish and you can run my-new-app.exe:

1_AnWAXmBXbtBOUu1iFJNAjg.png

Now we have a full ASP.NET Core/React/Typescript application running as a desktop application.

You can find the source code here:
https://github.com/megasoft78/asp-net-core-react-typescript-webview2

Time to create your killer app!

Good luck! 😉

Discover and read more posts from Gabriele Ferreri
get started
post commentsBe the first to share your opinion
David Navarro
3 months ago

An amazing explanation.
is it possible to create a full app, with a logic enclosed in the backend and a database and be able to distribute it?

Gabriele Ferreri
3 months ago

You can build a full app with the logic in the backend but you still need to have the frontend. Imagine this like a frontend/backend server run locally. You can embed the database if it’s something file based like SQLite but I’m not sure where to store to be able to write on it.

Sepp Vallant
5 months ago

Hi, thank you very much for the explanation.
How do you distribute this ?

Gabriele Ferreri
5 months ago

You can generate a self contained executable for the specific platform you are targeting:
https://learn.microsoft.com/en-us/dotnet/core/deploying/

Show more replies