Blazor WebAssembly, Hybrid, and Deployment
|
This section documents ASP.NET Core on .NET 10 (LTS), the current release — the minimal hosting model, the middleware pipeline, dependency injection, Minimal APIs, MVC & Razor Pages, Blazor with the current render modes, SignalR and gRPC, EF Core, ASP.NET Core Identity and policy-based authorization, output caching, rate limiting, and Native-AOT-aware building — as described by the official documentation at Microsoft Learn, which is the reference these pages are written and verified against. This content was generated with the assistance of AI and should be verified against the official documentation before being relied on in production. .NET ships a major release every November and its APIs continue to evolve: the examples here target .NET 10 / C# 14. This section’s bibliography lists the reference material consulted while preparing these pages. |
This page covers how Interactive WebAssembly and standalone Blazor WebAssembly apps actually run in the browser, how to host and deploy them, and how to reuse Blazor components outside a Blazor Web App entirely — in a .NET MAUI shell (Blazor Hybrid) or embedded in a non-Blazor page.
The WebAssembly runtime
A Blazor WebAssembly app downloads the .NET runtime itself (compiled to WebAssembly), the app’s assemblies, and any dependencies, then runs .NET IL in the browser with no plugin. First-load size and startup time are dominated by how much of that gets downloaded and JIT/AOT-compiled, which is what the rest of this section optimizes.
IL trimming and AOT compilation
IL trimming removes unused code from published assemblies (on by default for WebAssembly publish); AOT (ahead-of-time) compilation compiles .NET IL directly to WebAssembly instead of interpreting it at runtime, trading a larger download and slower build for substantially faster execution:
<PropertyGroup>
<RunAOTCompilation>true</RunAOTCompilation> <!-- set at publish time -->
<WasmEnableSIMD>true</WasmEnableSIMD> <!-- vectorized instructions where the browser supports them -->
</PropertyGroup>
AOT roughly doubles publish time and increases download size, so it pays off for CPU-bound client-side work more than for typical CRUD UI. SIMD lets AOT-compiled code use vectorized WebAssembly instructions on supporting browsers for further speedups on numeric workloads. See ASP.NET Core Blazor WebAssembly build tools and AOT and Ahead-of-time (AOT) compilation.
Lazy loading of assemblies
Defer assemblies not needed for the initial page, loading them only when a route that needs them is reached:
<ItemGroup>
<BlazorWebAssemblyLazyLoad Include="AdminModule.dll" />
</ItemGroup>
<Router AppAssembly="typeof(Program).Assembly" OnNavigateAsync="OnNavigateAsync">
...
</Router>
@code {
private async Task OnNavigateAsync(NavigationContext ctx)
{
if (ctx.Path.StartsWith("admin"))
{
await LazyAssemblyLoader.LoadAssembliesAsync(["AdminModule.dll"]);
}
}
}
Native dependencies
A WebAssembly app can reference native code compiled to WASM (a C/C++ library, or a .NET library with a native component) via the .NET WebAssembly build tools' native-relinking step — relevant mainly for performance-critical or existing native libraries with no managed equivalent; most apps never need this.
Fingerprinted static assets and preloaded framework assets (.NET 10)
Static Files and Asset Delivery) fingerprints the
WebAssembly runtime and app assemblies themselves, not just wwwroot content, so a browser caches them
indefinitely and only re-downloads what actually changed between deployments. The generated index.html
preloads the framework assets it knows it will need, reducing the number of sequential round trips before the
app becomes interactive.
Boot config
blazor.boot.json (generated at publish time) lists every assembly, its integrity hash, and load-time options
the WebAssembly bootstrapper reads before starting the runtime — inspect it when diagnosing "an assembly
failed to load" issues, but it is not meant to be hand-edited.
PWAs and service workers
dotnet new blazorwasm --pwa scaffolds a Progressive Web App: a manifest, a service worker
(service-worker.published.js) that caches the app shell for offline use, and installability on supporting
platforms. See
ASP.NET Core Blazor Progressive Web
Application (PWA).
Hosting standalone WASM vs. hosted
| Style | Notes |
|---|---|
Standalone, static host/CDN |
|
Hosted (ASP.NET Core-served) |
The WebAssembly client is served by (and typically calls back into) an ASP.NET Core project — the shape a Blazor Web App with WebAssembly/Auto interactivity takes. |
A static host needs to be configured to serve .wasm files with the application/wasm MIME type and to
rewrite unknown paths back to index.html for client-side routing to work on a hard refresh/deep link.
IIS / Nginx configuration for WASM
location / {
try_files $uri $uri/ /index.html;
}
location ~ \.wasm$ {
types { application/wasm wasm; }
}
IIS needs the web.config generated at publish time (which sets the application/wasm MIME type and a
web.config-driven fallback route) deployed alongside the static output. See
Deployment for the general IIS/Nginx hosting material this extends.
Blazor Hybrid with .NET MAUI
Blazor Hybrid hosts Razor components in a native app shell instead of a browser, via the BlazorWebView
control, sharing the same component model and much of the same code as a web Blazor app:
<!-- MainPage.xaml -->
<BlazorWebView HostPage="wwwroot/index.html" x:Name="blazorWebView">
<BlazorWebView.RootComponents>
<RootComponent Selector="#app" ComponentType="{x:Type local:Main}" />
</BlazorWebView.RootComponents>
</BlazorWebView>
Components run with full trust and full .NET access (file system, native APIs via platform-specific code) — there is no browser sandbox, so the "WebAssembly is not a trust boundary" caveat from
Blazor Security does not apply the same way, but the app still runs
on the end user’s device. Native interop goes through normal .NET MAUI platform APIs, not IJSRuntime,
though JS interop into the hosted web content still works for DOM-facing code. See
ASP.NET Core Blazor Hybrid.
Razor class libraries
A Razor class library (RCL) packages components, static assets, and CSS-isolated stylesheets for reuse across projects — a web app, a Blazor Hybrid app, and another web app can all reference the same RCL:
dotnet new razorclasslib -o SharedUi
dotnet add MyApp reference ../SharedUi
RCL components are just Razor components; an RCL exposing routable @page components needs its assembly added
to the host’s AdditionalAssemblies (see
Blazor Routing and Navigation). See
ASP.NET Core Razor class
library (RCL).
Adding Blazor to an existing MVC/Razor Pages/Angular/React app
Blazor components can be embedded into a page that is not itself a Blazor Web App by rendering them as custom elements — each acts as a normal HTML tag the host page’s own framework (or plain HTML) can drop in:
builder.RootComponents.RegisterCustomElement<Counter>("my-counter");
<my-counter increment="5"></my-counter>
<script src="_framework/blazor.webassembly.js"></script>
This is the supported path for incrementally adding an interactive island to an existing MVC, Razor Pages, Angular, or React app without a full rewrite. See Use Blazor components in JavaScript apps or SPA frameworks and the React Reference / the Angular Reference for the host-side frameworks.