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)

NET 10’s static web assets pipeline (see

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

dotnet new blazorwasm; publish output is static files (HTML/JS/WASM) with no server-side .NET at all — host on any static file server or 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.