How to Upgrade Your .NET WASM App from .NET 8 to .NET 10
Introduction
Upgrading a .NET WebAssembly (WASM) application from .NET 8 to .NET 10 unlocks significant performance improvements and simplifies deployment. The Copilot Studio team recently completed this migration, gaining automatic asset fingerprinting, reduced AOT output sizes, and a seamless transition. This guide walks you through the exact steps they followed, from updating project files to removing legacy build scripts. By the end, you’ll have a leaner, faster WASM app with fewer manual maintenance tasks.

What You Need
- .NET 10 SDK – Download from dotnet.microsoft.com
- An existing .NET 8 WASM project (Blazor WebAssembly or custom .NET WASM app)
- NuGet packages updated – Ensure all dependencies have versions compatible with .NET 10 (check each package’s release notes)
- Familiarity with your build pipeline – Especially any custom scripts for renaming WASM assets or handling integrity checks
- A test environment – Staging or development server to validate the upgrade before production
Step-by-Step Upgrade Process
Step 1: Update the Target Framework
Open your project file (.csproj) and change the <TargetFramework> from net8.0 to net10.0. For Blazor WebAssembly projects, it typically looks like:
<TargetFramework>net10.0</TargetFramework>
If your project uses multiple target frameworks, add or update the entry for net10.0. Save the file.
Step 2: Clean and Restore NuGet Packages
Run dotnet clean to remove previous build artifacts. Then execute dotnet restore to fetch .NET 10-compatible versions of all packages. Watch for any restore errors – they indicate incompatible dependencies. If an error appears, locate the offending package and either update it to a .NET 10-compatible version or replace it with an alternative.
Step 3: Remove Custom Fingerprinting Scripts
In .NET 8, many projects (like Copilot Studio) used a custom PowerShell script to add SHA256 hashes to WASM asset filenames and manually pass integrity arguments. With .NET 10, this is no longer needed. Automatic fingerprinting is built into the publish process – each asset gets a unique identifier in its filename, and integrity validation happens automatically via dotnet.js.
- Delete any PowerShell scripts, batch files, or custom build steps that rename files or compute hashes.
- Remove the integrity argument from your JavaScript resource loader. For example, if you previously wrote
fetch('app.dll', { integrity: 'sha256-...' }), you can simplify tofetch('app.dll'). - Update your deployment pipeline to skip any manual fingerprinting steps.
The Copilot Studio team deleted their entire custom renaming script and removed the integrity argument from the client-side loader – you can do the same. Existing caching logic built on top of these resources continues to work because the file names still change with each deployment.
Step 4: Enable AOT and Confirm WasmStripILAfterAOT
If you use Ahead-of-Time (AOT) compilation, .NET 10 enables WasmStripILAfterAOT by default. This removes the Intermediate Language (IL) for compiled methods, reducing the published output size. In .NET 8, this property defaulted to false. To leverage it in .NET 10, you don’t need to change anything – it’s already active.
To verify, open your project file and check that <WasmStripILAfterAOT> is either absent (defaults to true) or set to true. If you explicitly set it to false in .NET 8, remove that line or change it to true.

Note: If your application uses a hybrid approach as Copilot Studio does – shipping both a JIT engine and an AOT engine in a single NPM package – be aware that stripped AOT assemblies no longer match their JIT counterparts byte-for-byte. Fewer files can be deduplicated. This is expected and acceptable; the reduction in package size from stripping IL outweighs the minimal increase from deduplication loss.
Step 5: Test the Build
Run dotnet build and then dotnet publish (with the -c Release flag). Check the output directory – you should see fingerprint filenames like app.dll.abc123 instead of plain app.dll. Load the app in your browser and use developer tools to confirm assets are loaded with integrity checks (look for integrity attributes on the script tags). Test key user interactions to ensure performance is as expected.
Step 6: Update Client-Side Initialization (If Using WebWorkers)
If your app loads the .NET WASM runtime inside a WebWorker, you must set dotnetSidecar = true when initializing the runtime. This ensures proper initialization in a worker context. Add the parameter when calling createDotnetRuntime:
const dotnet = await createDotnetRuntime({
dotnetSidecar: true,
// other options
});
Without this, the runtime may fail to start inside the worker. This is a .NET 10-specific requirement; in .NET 8 it was optional.
Conclusion and Tips
- Back up your project before starting – create a Git branch or copy the folder.
- Test incrementally: After each step, try a build to catch issues early.
- Check dependency compatibility – Not all third-party libraries may have .NET 10 support yet. Visit each package’s NuGet page for version notes.
- Leverage automatic fingerprinting – It not only caches-busts but also provides integrity guarantees out of the box.
- For advanced scenarios (like Copilot Studio’s dual-engine setup), plan for slightly larger output if you use both JIT and AOT. The overall size decrease from stripping IL still gives a net gain.
- Monitor performance – Use browser profiling tools to compare startup time and runtime speed before and after the upgrade.
- Update CI/CD pipelines – Remove any custom steps for file renaming or integrity hash injection; your pipeline should now just publish normally.
With these steps, your .NET WASM application will be running on .NET 10, benefiting from faster builds, smaller downloads, and simpler deployment. Copilot Studio’s experience confirms that the migration is both smooth and rewarding.