VOOZH about

URL: https://thenewstack.io/no-more-javascript-how-microsoft-blazor-uses-webassembly/

⇱ No More JavaScript: How Microsoft Blazor Uses WebAssembly - The New Stack


TNS
SUBSCRIBE
Join our community of software engineering leaders and aspirational developers. Always stay in-the-know by getting the most important news and exclusive content delivered fresh to your inbox to learn more about at-scale software development.
REQUIRED
It seems that you've previously unsubscribed from our newsletter in the past. Click the button below to open the re-subscribe form in a new tab. When you're done, simply close that tab and continue with this form to complete your subscription.
The New Stack does not sell your information or share it with unaffiliated third parties. By continuing, you agree to our Terms of Use and Privacy Policy.
Welcome and thank you for joining The New Stack community!
Please answer a few simple questions to help us deliver the news and resources you are interested in.
REQUIRED
REQUIRED
REQUIRED
REQUIRED
REQUIRED
Great to meet you!
Tell us a bit about your job so we can cover the topics you find most relevant.
REQUIRED
REQUIRED
REQUIRED
REQUIRED
REQUIRED
Welcome!

We’re so glad you’re here. You can expect all the best TNS content to arrive Monday through Friday to keep you on top of the news and at the top of your game.

What’s next?

Check your inbox for a confirmation email where you can adjust your preferences and even join additional groups.

Follow TNS on your favorite social media networks.

Become a TNS follower on LinkedIn.

Check out the latest featured and trending stories while you wait for your first TNS newsletter.

PREV
1 of 2
NEXT
VOXPOP
As a JavaScript developer, what non-React tools do you use most often?
Angular
0%
Astro
0%
Svelte
0%
Vue.js
0%
Other
0%
I only use React
0%
I don't use JavaScript
0%
Thanks for your opinion! Subscribe below to get the final results, published exclusively in our TNS Update newsletter:
NEW! Try Stackie AI
From clobbered drafts to real-time sync
Apr 14th 2026 10:00am, by David Moore
TypeScript 6.0 RC arrives as a bridge to a faster future
Mar 14th 2026 9:00am, by Darryl K. Taft
Mastra empowers web devs to build AI agents in TypeScript
Jan 28th 2026 11:00am, by Loraine Lawson
2023-03-27 07:22:53
No More JavaScript: How Microsoft Blazor Uses WebAssembly
tutorial,
Frontend Development / Software Development / WebAssembly

No More JavaScript: How Microsoft Blazor Uses WebAssembly

David Eastman explains how Microsoft has smoothed the path for WebAssembly. You'll be tempted to leave JavaScript behind after this.
Mar 27th, 2023 7:22am by David Eastman
👁 Featued image for: No More JavaScript: How Microsoft Blazor Uses WebAssembly
Last week I introduced you to Blazor, Microsoft’s web stack that eschews JavaScript and enables developers to use WebAssembly on the client side. We saw quite a pleasing HTML/code separation on templates and a solid component system. Now it is time to venture further into the purple man’s domain (the presenter in the explainer videos, Jeff Fritz, wears a very fetching purple blazer) and beyond. You will remember previously that you can have multiple routes marked out on the page. We can see an interesting variant using this on the example Counter.razor:
<code class="language-csharp">
@page "/counter"

<PageTitle>Counter</PageTitle>

<h1>Counter</h1>

<p role="status">Current count: @currentCount</p>

<button class="btn btn-primary" @onclick="IncrementCount">Click me</button>

@code {
 private int currentCount = 0;

 private void IncrementCount()
 {
 currentCount++;
 }
}</code>
We can add another route to this one, which takes an argument that binds with StartingValue. You can see how readable this is:
<code class="language-csharp">
@page "/counter"
@page "/counter/{startingValue:int}

<PageTitle>Counter</PageTitle>

<h1>Counter</h1>

<p role="status">Current count: @currentCount</p>

<button class="btn btn-primary" @onclick="IncrementCount">Click me</button>

@code {
 private int currentCount = 0;

 [Parameter]
 public int StartingValue { get; set; }

 protected override void OnParametersSet() 
 {
 currentCount = StartingValue;
 base.OnParametersSet();
 }

 private void IncrementCount()
 {
 currentCount++;
 }
}</code>
This takes a parameter from the URL and puts it straight into the code (allowing for capitalization differences) using an override. Clearly, we are picking up an OnParameterSet event. Without the parameter present, the code works as before. If I do have a parameter that matches the type int, we get: 👁 Image
This sets us up for the trickier binding of different types of HTML UI elements to C# code. But for now, we will leave the Purple Man’s video lessons for a more pressing issue. This is the bit where we want to talk about the (purple) elephant in the room.

Are We Just Creating Server-Side Apps?

The answer is no. It is Microsoft’s business to create a unified development environment and blur the line between server-side and client-side. But they do make it clear they are different projects: 👁 Image
However, there are a few shiny but confusing baubles in the explanations for the two options above. WebAssembly directly supports .NET on the browser, and thus gives you offline behavior. In the Server App, no C# goes to the client at all. SignalR sounds like a toothpaste, but it is just asynchronous communication helper code for the client/server connection. You may also have noticed that the last sentence in both descriptions is identical. Microsoft is trying to square off its older systems with ways to support modern techniques while protecting its platform legacy.

Differences Between the Server and Wasm Apps

I want to compare the differences in approach between a server and a WebAssembly-based app by looking at an example component. So what are the differences? The demo project I have been using is, under the hood, a Blazer Server. So let’s look at the simple FetchData example. It shows some fake weather: 👁 Image
On the backend is this a simple fake service in pure C#:
<code class="language-csharp">public class WeatherForecastService
{
 private static readonly string[] Summaries = new[]
 {
 "Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching"
 };

 public Task<WeatherForecast[]> GetForecastAsync(DateOnly startDate)
 {
 return Task.FromResult(Enumerable.Range(1, 5).Select(index => new WeatherForecast
 {
 Date = startDate.AddDays(index),
 TemperatureC = Random.Shared.Next(-20, 55),
 Summary = Summaries[Random.Shared.Next(Summaries.Length)]
 }).ToArray());
 }
}</code>
Note the use of Random to generate results, as you might expect. Each time I refresh, I get some different fake data. Naturally, there is a FetchData.razor for the page, and it has two interesting parts:
<code class="language-csharp">
@page "/fetchdata"
@using FirstBlazorApp.Data
@inject WeatherForecastService ForecastService
...</code>
The inject directive is a form of Dependency Injection; Blazor uses this so that components can use available services in an independent manner — a real service in the wild would not be part of the calling program. Here is the corresponding code at the bottom of FetchData.razor:
<code class="language-csharp">
...
@code {
 private WeatherForecast[]? forecasts;

 protected override async Task OnInitializedAsync()
 {
 forecasts = await ForecastService.GetForecastAsync(DateOnly.FromDateTime(DateTime.Now));
 }
}
</code>
We know the rest of the code on the page is just going to loop through the array of WeatherForecast and display it. So we are just missing the code that “registers” this service. That is added to the boilerplate of Program.cs:
<code class="language-csharp">
...
var builder = WebApplication.CreateBuilder(args);

// Add services to the container.
builder.Services.AddRazorPages();
builder.Services.AddServerSideBlazor();
builder.Services.AddSingleton<WeatherForecastService>();
...</code>
It appears that the design treats the pages themselves as separate services. All components requiring a singleton service receive the same instance of the service. So in summary, we make a fake service for our server, register it, then “inject” it into the pages of the App. The client’s only responsibility is to display the information — and that is done in the rest of FetchData.razor by some simple HTML. This cannot possibly be the model for a WebAssembly client. So let’s pull in the WebAssembly equivalent of the demo project and compare the differences. And yes, there is a FetchData example in this project too. It runs the same as the server version, except there is a few seconds to wait for .NET to load into the browser. The data doesn’t change after refresh (we will see why shortly). This wait is acceptable as long as the page will claim the user’s interest for some amount of time. We can imagine that this will improve over time, too. Let’s start with the top of FetchData.razor:
<code class="language-csharp">
@page "/fetchdata"
@inject HttpClient Http
...</code>
Ah — so we are not injecting some backend fake service. Because, or course, there is no backend. In the demo, the method used is an HttpClient, so you can suck up some data in a nice REST service. How will that be applied?
<code class="language-csharp">
@code {
 private WeatherForecast[]? forecasts;

 protected override async Task OnInitializedAsync()
 {
 forecasts = await Http.GetFromJsonAsync<WeatherForecast[]>("sample-data/weather.json");
 }
 ...
}</code>
Ah, so the service is now an HTTP call for some trusty fake JSON. The static JSON file is sitting in the wwwroot directory of the project, and hence the data won’t refresh when the app runs:
<code class="language-csharp">[
 {
 "date": "2022-01-06",
 "temperatureC": 1,
 "summary": "Freezing"
 },
 {
 "date": "2022-01-07",
 "temperatureC": 14,
 "summary": "Bracing"
 },
 {
 "date": "2022-01-08",
 "temperatureC": -13,
 "summary": "Freezing"
 },
 {
 "date": "2022-01-09",
 "temperatureC": -16,
 "summary": "Balmy"
 },
 {
 "date": "2022-01-10",
 "temperatureC": -2,
 "summary": "Chilly"
 }
]</code>
We still need to find out how we register this service, and also find out why the HTTP started looking in our wwwroot. The answers happen to be on the same line:
<code class="language-csharp">...
var builder = WebAssemblyHostBuilder.CreateDefault(args);
builder.RootComponents.Add<App>("#app");
builder.RootComponents.Add<HeadOutlet>("head::after");

builder.Services.AddScoped(sp => new HttpClient { BaseAddress = new Uri(builder.HostEnvironment.BaseAddress) });</code>
So we add the HttpClient service and, in this case, the base address is set to the Host Environment. A Scoped service is tied up with the lifetime of an HTTP connection. While supporting subtle differences in approach, the two projects are largely the same. I hope this quick look at how Microsoft has smoothed the path for WebAssembly has tempted you to leave JavaScript behind and put on a colorful jacket.
TRENDING STORIES
David has been a London-based professional software developer with Oracle Corp. and British Telecom, and a consultant helping teams work in a more agile fashion. He wrote a book on UI design and has been writing technical articles ever since....
Read more from David Eastman
SHARE THIS STORY
TRENDING STORIES
SHARE THIS STORY
TRENDING STORIES
TNS DAILY NEWSLETTER Receive a free roundup of the most recent TNS articles in your inbox each day.
The New Stack does not sell your information or share it with unaffiliated third parties. By continuing, you agree to our Terms of Use and Privacy Policy.