Blog
Hosting Avalonia from D
Avalonia is not a widget catalog in the Qt or GTK sense.
Open a fresh project and you get panels, a button that is still a button,
a text box that is still a text box, and a styling system that feels like
writing HTML and CSS with extra steps.
That is a compliment to the engine and a warning to anyone who hoped to
import avalonia and receive a dashboard.
If you have hand-hacked Avalonia, you already know the texture.
A StackPanel is a div with a flex direction.
Classes.Add("Primary") is how you ask the theme for a filled
button.
There is no blessed settings shell waiting in the box.
You compose, or you look elsewhere.
That “elsewhere” already exists in C#.
ShadUI is a living MIT
library inspired by shadcn/ui and
Suki UI.
It ships ShadTheme, a custom window, cards, toasts, dialogs,
a sidebar—the kit people reach for when Avalonia’s primitives feel thin.
Cloning shadcn again, inside Avalonia, from D, would be a second copy of
a problem someone already solved on the C# side of the wall.
The wall
D cannot see C# types.
There is no extern (C#).
Avalonia has no C API.
So a “wrapper” that pretends GtkD happened—generate thousands of classes
from metadata, link, go—does not exist here.
What exists is hosting: a D main that loads a native shim,
the shim that boots CoreCLR, and a C# library that owns the
Application and the handle table.
avalonia-d
takes that shape on purpose.
The C# host starts Avalonia in code, adds ShadTheme, and
subclasses ShadUI.Window because the upstream constructor is
protected.
DNNE exports a small C ABI.
D dynamically loads the shim the same way other supplemental bindings
load a Zig core or a Rust bridge.
Handles are ulong.
Callbacks are extern (C) function pointers.
Forget that qualifier on the D aliases and the fourth
double in a margin call arrives as NaN.
That is an interop lesson, not a theming lesson.
What you write
import avalonia;
void main()
{
init(ThemeMode.dark);
auto win = new Window("Hello", 480, 280);
auto col = new Stack();
col.margin(24, 24, 24, 24);
auto btn = new Button("Click me");
btn.onClick({ /* ... */ });
col.add(btn);
win.content = col;
win.run();
}
D owns the process. Avalonia owns the pixels. ShadUI owns the look. You do not get every Avalonia type. You get a growing C ABI and idiomatic wrappers for the controls that have been walked across the boundary.
Fork ShadUI under this org when a C# control has to change. Until then, consume the NuGet and spend the D budget on the host and the handles. The interesting remaining work is more of the ShadUI surface on the ABI— dialogs, toasts, sidebar—without pretending the C# object graph is now a D object graph.
Read next: