Upgrading Your .NET WebAssembly App to .NET 10: A Step-by-Step Guide

Introduction

Microsoft Copilot Studio recently upgraded its .NET WebAssembly (WASM) engine from .NET 8 to .NET 10, unlocking significant performance and deployment improvements. This guide walks you through the same upgrade process for your own .NET WASM application, covering two key benefits: automatic fingerprinting of assets and reduced AOT output size via WasmStripILAfterAOT. By the end, you’ll have a faster, leaner app with simpler deployment—no custom scripts required.

Upgrading Your .NET WebAssembly App to .NET 10: A Step-by-Step Guide
Source: devblogs.microsoft.com

What You Need

  • .NET 10 SDK (available from dotnet.microsoft.com)
  • An existing .NET 8 Blazor WebAssembly project (or similar .NET WASM app)
  • A text editor or IDE (e.g., Visual Studio 2022, VS Code)
  • Basic familiarity with .csproj files and command-line publishing

Step-by-Step Guide

Step 1: Update the Target Framework

Open your project’s .csproj file and change the TargetFramework from net8.0 to net10.0. Also update any referenced NuGet packages that target .NET 8 to their .NET 10 compatible versions.

<PropertyGroup>
  <TargetFramework>net10.0</TargetFramework>
</PropertyGroup>

Tip: Run dotnet restore after the change to fetch updated dependencies.

Step 2: Verify Dependency Compatibility

Ensure all third-party libraries used in your project have .NET 10 versions. Check for breaking changes in Microsoft.AspNetCore.Components.WebAssembly and related packages. The migration from .NET 8 to .NET 10 is typically straightforward—Copilot Studio reported a smooth upgrade with no code changes beyond the framework update.

If you use custom NuGet feeds, update them to point to .NET 10 packages.

Step 3: Remove Custom Fingerprinting Logic

In .NET 8, you likely had a custom PowerShell script to rename WASM assets with SHA256 hashes for cache busting. In .NET 10, automatic fingerprinting is built in. Remove:

  • The PowerShell or batch script that appends hashes
  • Any JavaScript code that passes an explicit integrity argument when loading resources
  • Manual reading of blazor.boot.json to enumerate assets

Now, when you publish, every WASM asset’s filename automatically includes a unique identifier. The runtime (dotnet.js) handles integrity validation. Copilot Studio’s team deleted their entire custom renaming script—you can too.

Step 4: Enable AOT and Leverage Smaller Output

If you use Ahead-of-Time (AOT) compilation, .NET 10 now sets WasmStripILAfterAOT to true by default. This strips the Intermediate Language (IL) from compiled methods, reducing the download size. In your .csproj, you can explicitly enable AOT:

Upgrading Your .NET WebAssembly App to .NET 10: A Step-by-Step Guide
Source: devblogs.microsoft.com
<PropertyGroup>
  <RunAOTCompilation>true</RunAOTCompilation>
</PropertyGroup>

No further flags needed—the stripping happens automatically. Copilot Studio measured noticeable gains in startup time thanks to smaller AOT payloads.

Step 5: (Optional) Dual-Engine Packaging

If your app ships both a JIT and AOT engine (like Copilot Studio does in a single NPM package), note that WasmStripILAfterAOT causes AOT assemblies to differ from JIT ones. This may reduce opportunities for file deduplication. To mitigate, consider:

  • Keeping JIT and AOT in separate directories
  • Leveraging Brotli or Gzip compression to offset the size increase

Test your final package size and adjust if needed.

Step 6: Publish and Deploy

Run dotnet publish -c Release to generate the production build. Your output wwwroot folder will contain fingerprint-named files. Upload the contents to your CDN or web server. Existing caching and validation logic (e.g., Service Worker caching) will continue to work unchanged—only the filenames have changed.

Jump to Tips

Tips for a Smooth Upgrade

  • Test in a staging environment before deploying to production. The automatic fingerprinting can break cached assets if you haven’t updated your service worker’s cache name.
  • If you load the WASM runtime inside a Web Worker, set dotnetSidecar = true when calling Blazor.start() to ensure proper initialization in a worker context.
  • Monitor performance. Use browser DevTools to verify fingerprinting is working (look for unique filenames in the Network tab).
  • Consider incremental adoption. You can keep .NET 8 packages for some dependencies if they are compatible, but aim to upgrade all at once for consistent behavior.
  • Read the official .NET 10 migration docs for any additional breaking changes specific to your app.
Tags:

Recommended

Discover More

Rust Expands Mentorship: Joining Outreachy for 202610 Key Insights from the Trivy and KICS Supply Chain AttacksHow to Get Ready for macOS 27: A Step-by-Step Guide to Apple's Next Big UpdateOptimizing JavaScript Startup in V8: A Guide to Explicit Compile HintsHow Designers Can Make Accessibility a Natural Part of Their Workflow