Build an Avalonia app

This tutorial creates a desktop Fabulous 10 application, connects MVU state to Avalonia widgets, runs tests, and prepares a release build. Use the multi-platform template only when you also need Android or iOS hosts.

The template and standard Avalonia controls do not require a commercial control license. Avalonia also offers premium controls under a freemium model; read Avalonia licensing before adding extension controls.

Create and run

The fabulous-avalonia template also targets net10.0-android (and net10.0-ios on non-Linux hosts), so restoring it requires the android/ios .NET workloads. Without them, dotnet restore fails with an error such as NETSDK1147: ... workloads ... must be installed. If you only need a desktop app, use fabulous-avalonia-desktop instead of fabulous-avalonia in the command below. It targets only net10.0 and has no mobile workload requirement.

dotnet new install Fabulous.Avalonia.Templates
dotnet new fabulous-avalonia -n Counter
cd Counter
dotnet restore
dotnet run -c Debug

Confirm that template installation selected a 10.0.x package. The maintained Avalonia MVU CounterApp is the reference implementation: it uses Program.statefulWithCmd, tracing, exception handling, Component, layouts, events, and modifiers. The app selects DesktopApplication or SingleViewApplication at compile time, so shared UI stays in one file while each target keeps a small native host.

Change the app

Follow one message end to end: Button("Increment", Increment) dispatches Increment; update creates a model with a larger count; the component observes the program through Context.Mvu; and TextBlock receives the new value. Fabulous diffs that widget description against the previous one and updates the existing Avalonia controls.

Add another message and control using the same pattern. Use VStack, HStack, Grid, Dock, or Canvas according to the layout behavior you need. Modifiers such as .centerHorizontal() and .margin(20.) return a new typed widget builder and can be chained.

For larger flows, start with basic navigation, then compare component navigation and navigation path. The Gallery is the compiled catalog for controls, styling, gestures, and extension packages.

Test and publish

dotnet build -c Release
dotnet publish -c Release -f net10.0 -r linux-x64 --self-contained true

Important note: Certain versions of Fabulous.Avalonia.Templates generate Android host code that inherits from AvaloniaMainActivity. Newer Avalonia Android packages expose a non-generic AvaloniaMainActivity. If Android builds fail with FS0033 ("does not expect any type arguments"), update Platform/Android/MainActivity.fs to the API expected by the restored Avalonia package.

namespace Counter.Android

open Android.App
open Android.Runtime
open Avalonia
open Avalonia.Android

[<Android.App.Application>]
type AndroidApp(javaReference: nativeint, transfer: JniHandleOwnership) =
    inherit AvaloniaAndroidApplication<Avalonia.Application>(javaReference, transfer)

    override _.CustomizeAppBuilder(builder: AppBuilder) =
        base.CustomizeAppBuilder(Counter.App.create ())

[<Activity(Label = "Counter", MainLauncher = false)>]   // keep your generated attribute arguments
type MainActivity() =
    inherit AvaloniaMainActivity()

Before publishing, add pure update tests and an Avalonia headless test. The repository's TestableApp demonstrates both and captures a screenshot artifact in CI; see testing and debugging. Runtime identifiers and mobile hosts are covered in deployment.