louisjrdev

Using C# Web Assembly without using Blazor


This article was written on .NET6, it seems that with .NET7 there have been some updates to the WebAssembly packages since writing this, so the syntax is slightly different now.

I am working on an update for .NET7, in the meantime you can still follow this using the versions specified below.

So you may find yourself just wanting to share some of your C# code with your frontend JavaScript.

However, sharing just a few methods for use in your existing JavaScript setup seems to be near damn impossible without using a third-party solution like Uno.Wasm or something else.

Here is the solution I found digging around on the internet and StackOverflow

Solution

Create a new C# library project and replace the contents of the .csproj with the following, this just sets the SDK type and adds some NuGet packages we will need.

<Project Sdk="Microsoft.NET.Sdk.BlazorWebAssembly">
    <PropertyGroup>
        <TargetFramework>net6.0</TargetFramework>
        <LangVersion>10</LangVersion>
    </PropertyGroup>

    <ItemGroup>
        <PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly" Version="6.0.0" />
        <PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.DevServer" Version="6.0.0" PrivateAssets="all" />
    </ItemGroup>
</Project>

Now create a Program.cs class and add the following inside your class:

private static IJSRuntime js;

private static async Task Main (string[] args)
{
	var builder = WebAssemblyHostBuilder.CreateDefault(args);
	var host = builder.Build();
	js = host.Services.GetRequiredService<IJSRuntime>();
	await host.RunAsync();
}

[JSInvokable]
public static async Task<string> BuildGreeting(string greeting)
{
	var name = await GetNameViaJS();
	return $"{greeting}! {name} how are you?";
}

public static async Task<string> GetNameViaJS ()
{
	return await js.InvokeAsync<string>("getName");
}

Also add the following usings to your file or auto import the usings via your IDE:

using System.Threading.Tasks;
using Microsoft.AspNetCore.Components.WebAssembly.Hosting;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.JSInterop;

You can add a decorator of [JSInvokable] to the methods you want to expose to your JavaScript. To use a JavaScript method in your C# code, use your injected IJSRuntime and call InvokeAsync<T>.

Now publish this project using the dotnet publish command, this should output a folder at a path something like Project/Bin/Debug/net6.0/publish/wwwroot all of your compiled code you will need is in _framework/ . For now add an index.html file into your wwwroot folder and add the following to it:

<html lang="en">
	<head>
		<meta charset="UTF-8" />
		<meta http-equiv="X-UA-Compatible" content="IE=edge" />
		<meta name="viewport" content="width=device-width, initial-scale=1.0" />
		<title>Document</title>
	</head>
	<body></body>

	<script src="_framework/blazor.webassembly.js" autostart="false"></script>

	<script>

		window.getName = () =>
			return 'John';
		};

		window.onload = async function () {
			await Blazor.start();
			const greeting = await DotNet.invokeMethodAsync(
				'WasmMethods', //Your c# project namespace name
				'BuildGreeting',
				'Hello!'
			);
			alert(greeting);
		};
	</script>
</html>

Then serve this index.html file using a server of your choice, if you have node installed I recommend opening a terminal in the wwwroot folder and running npx http-server. This will automatically serve your index.html file at http://127.0.0.1:8080/

Now visit that URL and see that you get an alert popup saying Hello! John how are you?, this is using both the JS and C# code we defined earlier.

All in all this loads just over 70KB of resources over the wire and uncompressed that is just over 300KB.

Which is not nothing, so perhaps keep an eye on the dependencies you are loading in, as they can quickly bloat the resources transferred to the browser.