When you decide to build your .NET MAUI UI in C# instead of XAML, there are two serious options: CommunityToolkit.Maui.Markup and FmgLib.MauiMarkup. Both answer the same question, both use fluent extension methods, and both are MIT licensed.
I write this as the author of FmgLib.MauiMarkup — so I am biased, and I would rather say so up front. In exchange you will not find a marketing sentence here: every claim comes from the two projects' own public documentation, there is a dedicated section on where the Community Toolkit is the better choice, and at the end I installed both into one project to measure what actually happens.
Version numbers and download counts move quickly. This comparison reflects August 2026: FmgLib.MauiMarkup 10.3.0, CommunityToolkit.Maui.Markup 8.0.0. Check the sources before making a decision that is hard to reverse.
Where the differences come from: two architectural choices
Before any table, one sentence explains most of what follows:
- CommunityToolkit.Maui.Markup is a hand-written, curated set of extensions.
- FmgLib.MauiMarkup is produced at compile time by a Roslyn source generator.
The direct consequence: in the Toolkit, coverage depends on whether somebody wrote that helper. In FmgLib, coverage depends on whether the property exists in MAUI.
The second choice: the Toolkit sets up bindings through a separate .Bind(...) call, while FmgLib puts the binding inside the property you are already setting. Most of the differences below follow from these two.
Coverage: where does the chain break?
The Toolkit covers a dozen families — Label, Image, Grid, VisualElement, ItemsView, Placeholder and friends — and it is good at what it covers. But when nobody wrote a helper for the property you need, you drop into an object initializer mid-chain:
// CommunityToolkit — two styles mixed when a helper doesn't exist
new Entry
{
Keyboard = Keyboard.Numeric, // no fluent helper → object initializer
ReturnType = ReturnType.Done,
}
.Placeholder("Enter number") // helper exists → fluent
.FontSize(15)
.Height(44);
// FmgLib — every bindable property is generated, so the chain never breaks
new Entry()
.Keyboard(Keyboard.Numeric)
.ReturnType(ReturnType.Done)
.Placeholder("Enter number")
.FontSize(15)
.HeightRequest(44);
The same applies to events: FmgLib generates an On<Event> method for every event, while in the Toolkit events stay ordinary += subscriptions.
On its own this may not decide anything — object initializers are perfectly good C#. But as a codebase grows, “which properties have helpers?” becomes a question every contributor has to carry.
Third-party controls: the widest gap
In real apps a large share of the screens comes from Syncfusion, DevExpress, UraniumUI, SkiaSharp, ZXing and similar libraries.
With the Toolkit those controls only receive the generic VisualElement/View helpers; every control-specific property stays a plain assignment. In FmgLib the same generator works for them — either through a one-line MSBuild flag or per-control opt-in:
<MauiMarkupSourceGenerator>true</MauiMarkupSourceGenerator>
new SfButton().Text("Buy").CornerRadius(8) // Syncfusion
new SKLottieView().Source(…).RepeatCount(-1) // SkiaSharp.Extended
new CameraView().IsTorchOn(true).OnFrameReady(…) // ZXing
The generated methods are first-class: they accept bindings, work inside Style<T> and come with animation extensions. Attached properties of third-party controls are covered too.
Where does the binding live?
This is the second architectural choice, made visible:
// CommunityToolkit — the binding names the property again
new Entry().Bind(Entry.TextProperty,
static (ViewModel vm) => vm.RegistrationCode,
static (ViewModel vm, string text) => vm.RegistrationCode = text)
// FmgLib — the binding lives in the property you are already setting
new Entry().Text(e => e
.Getter(static (ViewModel vm) => vm.RegistrationCode)
.Setter(static (ViewModel vm, string text) => vm.RegistrationCode = text)
.BindingMode(BindingMode.TwoWay))
Both create compiled bindings and both support inline Convert/ConvertBack. The difference is whether you name Entry.TextProperty a second time, and where the binding visually belongs.
For multi-bindings the difference becomes one of typing. The Toolkit's FuncMultiConverter receives positional/object[] values; in FmgLib the delegate parameters are the sub-binding types themselves:
new Button().IsEnabled(e => e
.Path("AcceptedTerms")
.Path("ConfirmedEmail")
.MultiConvert((bool terms, bool email) => terms && email))
// compiled and string sub-bindings mix freely in one multi-binding
new Label().Text(e => e
.Getter(static (OrderVm vm) => vm.Total)
.Path("ItemCount")
.MultiConvert((decimal total, int count) => $"{count} items — {total:C}"))
Values that vary by context
FmgLib's property builder is not only about bindings; theme, device idiom and platform live in the same lambda:
new Label()
.TextColor(e => e.OnLight(Colors.Black).OnDark(Colors.White))
.FontSize(e => e.OnPhone(13.0).OnTablet(15.0).OnDesktop(17.0))
.Margin(e => e.OniOS(new Thickness(0, 20, 0, 0)).Default(new Thickness(0)))
.Text(e => e.Path("Title"))
OnLight/OnDark produces a real AppThemeBinding, so a running UI repaints itself when the theme changes — no page rebuild, no clearing and refilling of resource dictionaries. The Toolkit has AppThemeBinding/dynamic-resource helpers for theming, but not as a value inside the property call, and has no equivalent for idiom or platform.
What only FmgLib has
Topics with no counterpart in the Toolkit:
- Localization — JSON and RESX, live language switching, a culture fallback chain, formatted translations,
FlowDirectionfor RTL, a missing-key policy. The Toolkit ships no localization at all; you add a separate package and wire theINotifyPropertyChangedre-reads yourself. - Hot reload that re-runs your UI method — .NET Hot Reload applies to your code in both, but the Toolkit has nothing that re-invokes UI construction, so you usually reopen the page to see a markup edit. FmgLib's
Build()pattern rebuilds the UI on every applied edit, and registers pages weakly. - Generated
Animate<Property>Tofor every animatable property, awaitable. VisualState<T>with named state constants, plus animations that run on state entry.- Fluent triggers — property, data, multi and event.
- .NET 9 and .NET 10 from a single package version.
- A
dotnet newtemplate, a gallery sample app and documentation in English and Turkish.
When the Community Toolkit is the better choice
This section is not politeness. It is simply true.
Governance and continuity. The Toolkit is a .NET Foundation project. It outlives any individual maintainer, it is documented on Microsoft Learn, and it passes corporate approval processes easily. FmgLib is an independent project; in some organisations that alone is decisive, and reasonably so.
Maturity and community. There are two orders of magnitude between the download counts (~1 million vs ~19 thousand). That translates into the odds that your problem has been asked before, that a Stack Overflow answer exists, and that your next hire already knows the library.
A small API surface can be a feature. The Toolkit's scope is narrow but clear. In small teams, “everything has a fluent equivalent” can mean too many choices; a limited helper set enforces consistency by itself.
Build time. FmgLib's strength comes from a source generator, and generators are not free. In automatic mode, scanning many third-party libraries shows up in build times (opt-in mode limits this). The Toolkit has no such cost, because there is no generated code.
If you are already in the Toolkit ecosystem. If you use CommunityToolkit.Maui's behaviors, converters and popups, taking markup from the same family is a coherent choice.
Roughly: if coverage, third-party controls, localization or the hot reload loop are decisive for you, FmgLib; if governance, community size and minimum dependency risk are decisive, the Toolkit.
Can both live in one project?
This is the first question anyone considering a migration asks — so rather than guess, I measured it. I installed both, as real NuGet packages, into the same project.
Importing both namespaces in one file conflicts. Overlapping names become a build error:
error CS0121: The call is ambiguous between
'FmgLib.MauiMarkup.LabelExtension.Text<T>(T, string)' and
'CommunityToolkit.Maui.Markup.ElementExtensions.Text<TBindable>(TBindable, string?)'
The same happens for other overlapping names such as .Placeholder. This is expected: both add extension methods to MAUI types.
But separated per file, it compiles cleanly. Keeping both packages in one project and importing only one namespace per file built without a single error:
// NewPage.cs
using FmgLib.MauiMarkup; // FmgLib only
// LegacyPage.cs
using CommunityToolkit.Maui.Markup; // Community Toolkit only
The practical consequence: migration is not all-or-nothing. You can write new screens with the new library, leave existing screens untouched, and move file by file. Just make sure neither namespace is spread project-wide through ImplicitUsings or a global using — if it is, keeping a single namespace per conflicting file is cleaner than reaching for aliases.
Three questions to decide
- How much of what you use is MAUI's own controls? If the answer is “not most of it”, third-party generation closes the gap on its own.
- Are localization and theming product requirements? If so, the difference between built-in and do-it-yourself is measured in weeks.
- Does your organisation weigh governance when picking dependencies? If it does, the .NET Foundation label shortens the discussion.
Closing
Both libraries are the same idea applied at different scales: express the UI in C#, typed and refactorable. The Toolkit does it with a narrow, safe set; FmgLib extends the same idea across all of MAUI, third-party controls, theming, localization and the development loop.
Whichever you pick, the win of leaving XAML behind is shared: compiler support, refactoring safety, one language. The rest depends on your project's requirement list — and, as the experiment above shows, the choice is not irreversible.