505 GitHub stars and counting — AvaloniaUI/Avalonia.Markup.Declarative is a C# project TopGit is tracking across repositories on the platform. Provides helpers for declarative ui in C#
Snapshot summary built from the project's own GitHub metadata — there's no written TopGit review yet. The page will update automatically when a full review is published.
WHY NO REVIEW YET
TopGit writes full reviews for the most-starred, most-requested repositories. This page is a snapshot until then — see the READ ME tab for the original README in full.
Important: This repository is community driven, not officially supported by avalonia team and is not part of the official Avalonia project—it's only a proof of concept demonstrating how markup can be written purely in C#. For real-world projects use Avalonia's supported XAML approach.
Avalonia.Markup.Declarative
Write Avalonia UI with C# like a Boss 🗿
Avalonia.Markup.Declarative is a C#-first authoring layer over Avalonia controls. The current API is compiled-binding-first and source-generator-driven, with public patterns intentionally aligned with Avalonia's DataContext, binding, style, and selector model.
real projects that using Avalonia.Markup.Declarative
https://github.com/gritsenko/pix2d - Pix2d graphic editor for indie developers
feel free to add your project
Installation
Add the Avalonia.Markup.Declarative NuGet package to your project
Project Template
You can easily create a new project from the command line using the official template:
dotnet new install Declarative.Avalonia.Templates
dotnet new avalonia-declarative -n MyApp
Use a self-contained declarative component when the view and its reactive state belong to the same feature. Add CommunityToolkit.Mvvm to the app project and keep the component-local state in a nested ObservableObject.
using Avalonia.Data;
using CommunityToolkit.Mvvm.ComponentModel;
public class CounterComponent() : ViewBase<CounterComponent.State>(new State())
{
public sealed partial class State : ObservableObject
{
[ObservableProperty]
[NotifyPropertyChangedFor(nameof(CounterLabel))]
public partial decimal? Counter { get; set; } = 0;
[ObservableProperty]
public partial string StatusText { get; set; } = "Hello world";
public string CounterLabel => $"Counter: {Counter}";
}
protected override object Build(State state) =>
new StackPanel()
.Children(
new TextBlock()
.Text(state, x => x.StatusText),
new TextBlock()
.Text(state, x => x.CounterLabel),
new NumericUpDown()
.Value(state, x => x.Counter, BindingMode.TwoWay),
new Button()
.Content("Increment")
.OnClick(_ => state.Counter++)
);
}
To compose constructor-injected views, prefer ViewFactory.Create<T>(). If you use DI, register UseComponentControlFactory(...) on AppBuilder.
MVVM Pattern implementation
Use ViewBase<TViewModel> when you want a classic Avalonia or WPF-style view model. Generated setters expose compiled-binding overloads, so the binding syntax stays close to native Avalonia.
using Avalonia.Data;
public class MainView() : ViewBase<MainViewModel>(new MainViewModel())
{
protected override object Build(MainViewModel vm) =>
new StackPanel()
.Children(
new TextBox()
.Text(vm, x => x.Message, BindingMode.TwoWay),
new TextBlock()
.Text(vm, x => x.Message),
new Button()
.Content("Reset")
.OnClick(_ => vm.Message = string.Empty)
);
}
If you assign DataContext from the outside, the same generated setters also support DataContext-relative compiled bindings such as new TextBlock().Text<MainViewModel>(x => x.Message);.
Generated compiled-binding setters also apply automatic conversion for common primitive and nullable mismatches, so bindings like new Slider().Value(vm, x => x.Counter, BindingMode.TwoWay) work when Counter is int, and new CheckBox().IsChecked(vm, x => x.Enabled) work when Enabled is bool. Prefer plain member access such as x => x.Counter: a numeric-conversion cast like x => (double)x.Counter is both unnecessary (the auto-converter handles it) and unsupported — Avalonia's expression parser rejects value-converting Convert nodes. Type casts that navigate to a member of a derived type, such as x => ((DerivedType)x).Property, are supported. For lossy numeric TwoWay conversions, convert-back truncates toward zero.
Passing a ready-made binding
When you need the full binding feature set — reflection bindings (Binding), a pre-built compiled binding, a TemplateBinding, a MultiBinding, or a relative-source/element-name binding — every generated property, attached-property and style setter also exposes an overload that accepts a BindingBase directly:
using Avalonia.Data;
new TextBlock()
.Text(new Binding("ReflectionProperty")) // DataContext-relative reflection binding
.Foreground(new Binding("Theme.Accent") { Source = appState }); // explicit source
new TextBlock()
.Text(CompiledBinding.Create<MyViewModel, string>(x => x.Title, source: vm));
// attached properties and styles get the same overload
new Border().Grid_Row(new Binding(nameof(vm.Row)) { Source = vm });
new Style<TextBlock>().Text(new Binding(nameof(vm.Name)));
This is the escape hatch for anything the strongly-typed x => x.Member expression overloads don't cover (custom converters passed on the binding, RelativeSource, ElementName, string format, multi-value bindings, etc.). The same thing is available on any AvaloniaProperty via control.BindValue(TextBlock.TextProperty, binding).
Hot reload support
ViewBase supports .NET 6.0+ hot reload.
Keeping declarative views in an assembly without XAML can still produce the smoothest hot reload experience.
Current Avalonia and .NET toolchains are much better at mixing AXAML and C# markup in the same application, so the limitation is much smaller than it used to be.
The optional, dev-onlyDeclarative.Avalonia.AgentTools package runs an in-process MCP (Model
Context Protocol) server on loopback in debug builds, so an AI agent iterating on your UI can see and
drive the running app — closing the edit → hot-reload → verify loop without a human relaying what the
window looks like.
using Declarative.Avalonia.AgentTools; // under #if DEBUG
AppBuilder.Configure<App>()
.UsePlatformDetect()
#if DEBUG
.UseAgentInspector() // loopback MCP server on 127.0.0.1:5599
#endif
.SetupWithLifetime(lifetime);
It exposes screenshots (with before/after pixel-diffing), the visual tree with bounds and a single
absolute client-DIP coordinate frame (abs/center) shared by every tool, per-control layout reports,
an automated layout audit, property / property-source / view-model inspection, pixel↔control hit-testing
(with a "how to drive this control" hint), and recent build/binding/runtime errors. An opt-in tier
(EnableInteraction) also drives the app — real synthesized pointer/keyboard input (tap, drag,
pointer_*, that work even on custom controls with no automation peer), click, type, select, resize,
switch theme, open a closed popup — plus an escape hatch (with structured, actionable errors) to set a
view-model property or run a command directly to reach awkward states.
Keep the call under #if DEBUG: the package pulls in a web stack and a remote-control surface and must not
ship in Release; it binds to loopback only. See docs/agent-tools.md for the full
guide.
Enable the MCP in your agent
The inspector is a streamable-HTTP MCP server on http://127.0.0.1:5599, so every agent points at the
same URL. Run the app under dotnet watch so the agent's edits hot-reload into the process it inspects.
Claude Code — claude mcp add --transport http avalonia-agent-inspector http://127.0.0.1:5599, or a
project .mcp.json:
Codex — an [mcp_servers.*] table in ~/.codex/config.toml (a url makes it streamable-HTTP; enable
the RMCP client once with [features] → experimental_use_rmcp_client = true):
Make sure that the path to the source generator project is correct relative to your project.
Note: If you are using this library as a NuGet package, the source generator is included automatically.
External libraries support
Framework extensions are generated automatically for the supported Avalonia assemblies that your project references. They are internal by default. Libraries that need their generated extensions to be consumed by another project can opt into public extensions:
using Avalonia.Markup.Declarative;
[assembly: GenerateMarkupExtensionsForAvalonia(true)]
Only one library in an application's dependency graph should publish public extensions for Avalonia controls. Consumers should rely on the extensions from that library rather than opt into public Avalonia extensions themselves. The generator skips local duplicates when it finds one referenced provider and reports warning AMDGEN001 with the assembly names and an example control when multiple referenced libraries provide them. If you own the libraries, opt into public generation in just one of them. To generate extensions for a third-party library, add an assembly attribute that points to any type from that assembly:
using Avalonia.Markup.Declarative;
using ReactiveUI.Avalonia;
[assembly: GenerateMarkupExtensionsForAssembly(typeof(RoutedViewHost))]
No standalone tool installation or manual avalonia-amd-gen step is required anymore.
Does AvaloniaUI/Avalonia.Markup.Declarative have any tags?
TopGit's last sync did not record any GitHub topics for AvaloniaUI/Avalonia.Markup.Declarative. GitHub topics appear in the right sidebar of a repository page; that's the authoritative place to check.
How active is development on AvaloniaUI/Avalonia.Markup.Declarative?
The most recent commit recorded on AvaloniaUI/Avalonia.Markup.Declarative was 27 days ago, based on the GitHub push timestamp. The repository has 27 forks — one of the better signals of community interest.
How many stars does AvaloniaUI/Avalonia.Markup.Declarative have?
AvaloniaUI/Avalonia.Markup.Declarative has 505 GitHub stars — refresh the page for the live number, or check github.com/AvaloniaUI/Avalonia.Markup.Declarative. TopGit mirrors GitHub's count but does not claim minute-by-minute accuracy.
What language is AvaloniaUI/Avalonia.Markup.Declarative written in?
AvaloniaUI/Avalonia.Markup.Declarative is written primarily in C#. GitHub's language field is based on the largest share of bytes in the default branch.
Where do I read more about AvaloniaUI/Avalonia.Markup.Declarative?
This TopGit page is a snapshot — the READ ME tab shows the project's own README content (links stripped, images preserved). The GitHub repository at github.com/AvaloniaUI/Avalonia.Markup.Declarative is the definitive source.
Read full README in the tab above.
Is Avalonia.Markup.Declarative worth your time?
ChatGPT, Claude and Perplexity can all read this page. Ask one of them what it makes of Avalonia.Markup.Declarative.