Avalonia for WebAssembly
The real .NET runtime, your existing C# and XAML, drawn onto a canvas and served as static files. The same UI code that runs your desktop build.
Platform support
Your desktop application, served as static files.
Avalonia compiles the C# and XAML you already have to WebAssembly and draws it onto a canvas. There’s no HTML to author, no CSS cascade to fight and no server-side component to deploy.
Hosting
Static files
No server-side component is required. Any static host will do.
Rendering
Skia through CanvasKit
Drawn to a WebGL canvas, so there’s no DOM to fight.
Browsers
Anything with full WebAssembly
The docs defer to caniuse.com/wasm rather than pinning versions.
Runtime
.NET 8.0 or later
The real .NET runtime, the real base class library, in the tab.
Running in this tab
This is Avalonia, compiled to WebAssembly
What opens is the Avalonia Charts control, running on the real .NET runtime in this tab. No plugin, no server rendering the frames, and no second UI stack.
The live demo runs on a desktop browser, where spending the download makes sense. Everything describing it below is the same on a phone.
Not a second product
The browser is a publish target, not a second front end.
WebAssembly is another head on the project you already maintain. The view models, the styles, the custom controls and the validation rules are the ones your desktop build uses, because they’re literally the same assemblies.
That removes the usual cost of a web version: a second UI stack, a second place to fix a bug and a second design system to keep in step. The output is a folder of static files, so it deploys to object storage, a CDN or whatever already serves your marketing site.
dotnet publish -c Release
# bin/Release/net8.0-browserwasm/publish/wwwrootWhat you get in the tab
The real runtime, not a subset of it.
Avalonia on WebAssembly runs on the .NET runtime rather than on a translation layer, so the language and the library surface are the ones you already know.
The base class library
System.Text.Json, HttpClient, LINQ, generics, async and the rest of the BCL behave the way they do everywhere else, because it’s the same runtime.
NuGet packages
If a package compiles against .NET and doesn’t need a platform capability the browser withholds, it works, using the same package reference as the desktop build.
JavaScript interop where you want it
Call browser APIs or an existing JavaScript library through .NET’s JS interop when the web platform genuinely has something to offer, without restructuring the interface to get there.
Identical rendering
Skia draws through CanvasKit onto a WebGL canvas, so the browser build looks like the desktop build, not an HTML approximation of it.
Every component page runs its control live in the browser, which is itself Avalonia compiled to WebAssembly.
Try more demosWhere it meets the web
Avalonia owns the canvas. The browser is still right there.
Drawing the interface ourselves is what makes the browser build match the desktop one. It doesn’t put the web platform out of reach when you actually want it.
Trimmed by default
Release builds trim automatically, so the linker drops the parts of your assemblies and the framework that your application never calls. That’s size you don’t have to negotiate for.
The compression does real work
The docs put Brotli at a 60 to 70 percent reduction on the .wasm files specifically, and most static hosts apply it without being asked. Serve it with long-lived cache headers and the cost lands once per release.
Debug it like the desktop build
DevTools inspects the running visual tree, styles and bindings of the browser build with the same tool you use on the desktop one, because underneath it’s the same tree.
Publishing, hosting and the interop attribute reference are all documented.
WebAssembly guideGetting it deployed
Static files, and one thing to plan for.
The build output is a wwwroot folder. What it costs to deliver that folder is the part worth designing around, so here it is plainly.
Any static host
GitHub Pages, the nginx already running your main site, or any other static host you’ve got. No server-side component is required.
One MIME type to get right
Your server has to send .wasm as application/wasm. Nearly everything modern already does, and it’s the first thing to check when a deployment works everywhere except one environment.
Cache it once per release
The payload is content-addressed and immutable, so long-lived cache headers move the cost from once per visit to once per deploy. That single decision is most of the perceived-speed story.
The honest part: first load
The runtime and your assemblies have to reach the browser before the first frame. Avalonia on the web suits applications people return to, not a landing page that must paint in 200ms.
Good fits
Internal tools, operator consoles, dashboards, demos of desktop software, and anything where a login already precedes the first screen.
Preload what you can
Fetch the runtime while the visitor is still reading, and boot it when they look like they’re about to interact. The demo higher up this page does exactly that, which is why it opens as fast as it does.
Add the browser head to an existing Avalonia solution and publish it to a static host in an afternoon.
Get startedEvery target
The browser is the sixth target.
Nothing about the browser build is a separate codebase. It’s the desktop and mobile project with one more head added to the solution.
Windows
Direct Win32 calls, x64 and ARM64.
macOS
A native backend, universal binaries.
Linux
X11, Wayland, and bare framebuffer.
iOS
The same views, drawn on Metal.
Android
ARM64, ARM32 and x64 devices.
WebAssembly
Static files, no server component.
You’re here
Publish the same demo you just tried.
Add a browser head to an existing Avalonia project and publish the wwwroot folder to any static host. The framework is MIT licensed, and there’s no server to provision before people can use it.