.NET 9 improves JavaScript module importing for Blazor
November 13, 2024 · 3 minute read · Tags: aspnet | blazor | net9
So you’ve set out to build your shiny new web app using Blazor.
Blazor’s component model is more than up to the task and you spend a few weeks happily building out UI to meet your requirements.
But then you run into a requirement to integrate with a JavaScript library.
Curse it! Now you have to figure out how to make calls to a JavaScript library from your Blazor app.
This is exactly the kind of requirement that can get complicated, pretty quickly.
Especially if the JS dependency is packaged up as a JavaScript module.
Thankfully, importing JS modules into your Blazor app has become easier with recent releases, and .NET 9 addresses one of the biggest challenges.
Say we have a small JS module for showing a ’toast’ notification.
// JS/toast.js
export class NotificationHelper {
async showToast(message, type = 'info', options = {}) {
// details omitted
}
}
export default new NotificationHelper();
In your Blazor component, if you’re using Blazor WASM or Blazor Server, you can import this module and invoke its methods like this:
@inject IJSRuntime JSRuntime;
<button @onclick="@(()=>ShowNotification("Hello World"))">
Show Notification
</button>
@code {
async Task ShowNotification(string message)
{
var module = await
JSRuntime.InvokeAsync<IJSObjectReference>
("import", "./js/toast.js");
await module.InvokeVoidAsync(
"default.showToast", message, "success");
}
}
When a user clicks the button this will:
- Load the contents of
toast.js
and import it as a JS module. - invoke the
showToast
method on the default export from that JS module (NotificationHelper
)
But what if you make changes to that JS script and re-deploy your app.
Just as we saw last time with stylesheets, the browser will try to cache the JS module and your users will likely find themselves stuck with an old version.
Cue chaos and confusion!
Thankfully .NET 9 has a solution.
When you switch to the new static asset middleware:
app.MapStaticAssets();
You can also add something called ImportMap
to your App.razor page.
<head>
<!-- existing code-->
<ImportMap />
</head>
Run this in the browser, view the page source, and you’ll see that Blazor has identified all the JS modules in your app, and created an import map for them.
<script type="importmap">{
"imports": {
// other imports
"./js/toast.js": "./js/toast.voa2fr9sx9.js",
}
}</script>
When you publish your app .NET 9 creates a fingerprinted version of your JS modules, with a unique filename based on their contents.
./js/toast.voa2fr9sx9.js in this example.
The import map then ensures any attempt to import these modules is redirected to the fingerprinted version.
You can see this in the browser dev tools (assuming the component is running via Blazor WASM).
Note how the request for the JS file is using the fingerprinted filename.
With this your users will always get the latest version.
Oh, and its worth noting the new static asset middleware compresses these resources by default too, so the browser gets to download significantly smaller JS and CSS files.
– Jon